init
This commit is contained in:
commit
77064a9563
79 changed files with 10533 additions and 0 deletions
46
2022/lib/day.ex
Normal file
46
2022/lib/day.ex
Normal file
|
@ -0,0 +1,46 @@
|
|||
defmodule AOC.Day do
|
||||
defmacro __using__(opts) do
|
||||
day = Keyword.get(opts, :day)
|
||||
debug = Keyword.get(opts, :debug, false)
|
||||
trim = Keyword.get(opts, :trim, true)
|
||||
input_file = Keyword.get(opts, :input, nil)
|
||||
|
||||
quote do
|
||||
def solve do
|
||||
input =
|
||||
AOC.Day.input_lines(unquote(day), unquote(trim), unquote(input_file))
|
||||
|> parse_input()
|
||||
|
||||
if unquote(debug) do
|
||||
IO.inspect(input)
|
||||
end
|
||||
|
||||
part1_solution = part1(input)
|
||||
IO.puts("Part 1: #{part1_solution}")
|
||||
|
||||
part2_solution = part2(input)
|
||||
IO.puts("Part 2: #{part2_solution}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def input_lines(day, trim, input_file) do
|
||||
lines =
|
||||
input_file_name(day, input_file)
|
||||
|> File.stream!()
|
||||
|
||||
if trim do
|
||||
Enum.map(lines, &String.trim/1)
|
||||
else
|
||||
lines
|
||||
end
|
||||
end
|
||||
|
||||
def input_file_name(day, input_file) do
|
||||
if input_file do
|
||||
Path.join([File.cwd!(), "inputs", "day#{day}_#{input_file}.txt"])
|
||||
else
|
||||
Path.join([File.cwd!(), "inputs", "day#{day}.txt"])
|
||||
end
|
||||
end
|
||||
end
|
24
2022/lib/days/day1.ex
Normal file
24
2022/lib/days/day1.ex
Normal file
|
@ -0,0 +1,24 @@
|
|||
defmodule AOC.Day1 do
|
||||
use AOC.Day, day: 1
|
||||
|
||||
def parse_input(lines) do
|
||||
lines
|
||||
|> Enum.chunk_by(&(&1 == ""))
|
||||
|> Enum.reject(&(&1 == [""]))
|
||||
|> Enum.map(fn l -> Enum.map(l, &String.to_integer/1) end)
|
||||
end
|
||||
|
||||
def part1(input) do
|
||||
input
|
||||
|> Enum.map(&Enum.sum/1)
|
||||
|> Enum.max()
|
||||
end
|
||||
|
||||
def part2(input) do
|
||||
input
|
||||
|> Enum.map(&Enum.sum/1)
|
||||
|> Enum.sort(:desc)
|
||||
|> Enum.take(3)
|
||||
|> Enum.sum()
|
||||
end
|
||||
end
|
93
2022/lib/days/day11.ex
Normal file
93
2022/lib/days/day11.ex
Normal file
|
@ -0,0 +1,93 @@
|
|||
defmodule AOC.Day11 do
|
||||
use AOC.Day, day: 11
|
||||
alias AOC.Day11.{MonkeyRegistry, Monkey}
|
||||
|
||||
def parse_input(lines) do
|
||||
monkeys =
|
||||
lines
|
||||
|> Enum.chunk_by(&(&1 == ""))
|
||||
|> Enum.reject(&(&1 == [""]))
|
||||
|> Enum.map(&parse_monkey/1)
|
||||
|
||||
supermod =
|
||||
monkeys
|
||||
|> Enum.map(& &1.modulo)
|
||||
|> Enum.reduce(&Kernel.*/2)
|
||||
|
||||
{:ok, _} = Registry.start_link(keys: :unique, name: MonkeyRegistry)
|
||||
|
||||
Enum.each(monkeys, fn %{id: id} = monkey ->
|
||||
GenServer.start_link(Monkey, Map.put(monkey, :supermod, supermod),
|
||||
name: {:via, Registry, {MonkeyRegistry, id}}
|
||||
)
|
||||
end)
|
||||
|
||||
Enum.count(monkeys)
|
||||
end
|
||||
|
||||
def parse_monkey([
|
||||
"Monkey " <> monkey_id,
|
||||
"Starting items: " <> items,
|
||||
"Operation: " <> operation,
|
||||
"Test: divisible by " <> modulo,
|
||||
"If true: throw to monkey " <> true_monkey_id,
|
||||
"If false: throw to monkey " <> false_monkey_id
|
||||
]) do
|
||||
%{
|
||||
id: monkey_id |> String.slice(0..-2) |> String.to_integer(),
|
||||
items: items |> String.split(", ") |> Enum.map(&String.to_integer/1),
|
||||
operation: parse_operation(operation),
|
||||
modulo: String.to_integer(modulo),
|
||||
true_monkey_id: String.to_integer(true_monkey_id),
|
||||
false_monkey_id: String.to_integer(false_monkey_id)
|
||||
}
|
||||
end
|
||||
|
||||
def parse_operation(operation) do
|
||||
["new", "=", lhs, op, rhs] = String.split(operation)
|
||||
|
||||
fn old ->
|
||||
lhs = if lhs == "old", do: old, else: String.to_integer(lhs)
|
||||
rhs = if rhs == "old", do: old, else: String.to_integer(rhs)
|
||||
|
||||
op =
|
||||
case op do
|
||||
"*" -> &Kernel.*/2
|
||||
"+" -> &Kernel.+/2
|
||||
end
|
||||
|
||||
op.(lhs, rhs)
|
||||
end
|
||||
end
|
||||
|
||||
def part1(monkey_count) do
|
||||
# monkey_business(monkey_count, 20, false)
|
||||
end
|
||||
|
||||
def part2(monkey_count) do
|
||||
monkey_business(monkey_count, 10000, true)
|
||||
end
|
||||
|
||||
def monkey_business(monkey_count, rounds, ridiculous) do
|
||||
execute_rounds(monkey_count, rounds, ridiculous)
|
||||
|
||||
Enum.map(0..(monkey_count - 1), fn id ->
|
||||
[{pid, _}] = Registry.lookup(MonkeyRegistry, id)
|
||||
Monkey.get_activeness(pid)
|
||||
end)
|
||||
|> Enum.sort(:desc)
|
||||
|> Enum.take(2)
|
||||
|> Enum.reduce(&Kernel.*/2)
|
||||
end
|
||||
|
||||
def execute_rounds(monkey_count, rounds, ridiculous) do
|
||||
Enum.each(0..(rounds - 1), fn _ -> execute_round(monkey_count, ridiculous) end)
|
||||
end
|
||||
|
||||
def execute_round(monkey_count, ridiculous) do
|
||||
Enum.each(0..(monkey_count - 1), fn id ->
|
||||
[{pid, _}] = Registry.lookup(MonkeyRegistry, id)
|
||||
Monkey.execute_turn(pid, ridiculous)
|
||||
end)
|
||||
end
|
||||
end
|
63
2022/lib/days/day11/monkey.ex
Normal file
63
2022/lib/days/day11/monkey.ex
Normal file
|
@ -0,0 +1,63 @@
|
|||
defmodule AOC.Day11.Monkey do
|
||||
use GenServer
|
||||
alias AOC.Day11.{MonkeyRegistry, Monkey}
|
||||
|
||||
@impl true
|
||||
def init(state) do
|
||||
{:ok, Map.put(state, :activeness, 0)}
|
||||
end
|
||||
|
||||
def execute_turn(pid, ridiculous) do
|
||||
GenServer.call(pid, {:execute_turn, ridiculous}, :infinity)
|
||||
end
|
||||
|
||||
def throw(pid, item) do
|
||||
GenServer.call(pid, {:throw, item})
|
||||
end
|
||||
|
||||
def get_items(pid) do
|
||||
GenServer.call(pid, :get_items)
|
||||
end
|
||||
|
||||
def get_activeness(pid) do
|
||||
GenServer.call(pid, :get_activeness)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(
|
||||
{:execute_turn, ridiculous},
|
||||
_from,
|
||||
%{
|
||||
items: items,
|
||||
operation: operation,
|
||||
modulo: modulo,
|
||||
true_monkey_id: true_monkey_id,
|
||||
false_monkey_id: false_monkey_id,
|
||||
activeness: activeness,
|
||||
supermod: supermod
|
||||
} = state
|
||||
) do
|
||||
Enum.each(items, fn item ->
|
||||
item = operation.(item)
|
||||
item = if ridiculous, do: item, else: Integer.floor_div(item, 3)
|
||||
item = Integer.mod(item, supermod)
|
||||
recipient = if Integer.mod(item, modulo) == 0, do: true_monkey_id, else: false_monkey_id
|
||||
[{pid, _}] = Registry.lookup(MonkeyRegistry, recipient)
|
||||
Monkey.throw(pid, item)
|
||||
end)
|
||||
|
||||
{:reply, :ok, %{state | items: [], activeness: activeness + length(items)}}
|
||||
end
|
||||
|
||||
def handle_call({:throw, item}, _from, %{items: items} = state) do
|
||||
{:reply, :ok, Map.put(state, :items, items ++ [item])}
|
||||
end
|
||||
|
||||
def handle_call(:get_items, _from, %{items: items} = state) do
|
||||
{:reply, items, state}
|
||||
end
|
||||
|
||||
def handle_call(:get_activeness, _from, %{activeness: activeness} = state) do
|
||||
{:reply, activeness, state}
|
||||
end
|
||||
end
|
67
2022/lib/days/day13.ex
Normal file
67
2022/lib/days/day13.ex
Normal file
|
@ -0,0 +1,67 @@
|
|||
defmodule AOC.Day13 do
|
||||
use AOC.Day, day: 13
|
||||
alias AOC.Day13.Packet
|
||||
|
||||
defmodule Packet do
|
||||
def compare({p1, p2}) do
|
||||
compare(p1, p2)
|
||||
end
|
||||
|
||||
def compare([], []), do: :eq
|
||||
def compare([], _l), do: :lt
|
||||
def compare(_l, []), do: :gt
|
||||
def compare(x, l) when is_number(x) and is_list(l), do: compare([x], l)
|
||||
def compare(l, x) when is_number(x) and is_list(l), do: compare(l, [x])
|
||||
|
||||
def compare([h1 | tl1], [h2 | tl2]) do
|
||||
case compare(h1, h2) do
|
||||
:eq -> compare(tl1, tl2)
|
||||
x -> x
|
||||
end
|
||||
end
|
||||
|
||||
def compare(x, y) when x < y, do: :lt
|
||||
def compare(x, y) when x == y, do: :eq
|
||||
def compare(x, y) when x > y, do: :gt
|
||||
end
|
||||
|
||||
def parse_input(lines) do
|
||||
lines
|
||||
|> Enum.chunk_by(&(&1 == ""))
|
||||
|> Enum.reject(&(&1 == [""]))
|
||||
|> Enum.map(fn [p1, p2] ->
|
||||
{parse_packet(p1), parse_packet(p2)}
|
||||
end)
|
||||
end
|
||||
|
||||
def parse_packet(packet) do
|
||||
packet
|
||||
|> String.to_charlist()
|
||||
|> Code.string_to_quoted()
|
||||
|> elem(1)
|
||||
end
|
||||
|
||||
def part1(pairs) do
|
||||
pairs
|
||||
|> Enum.map(&Packet.compare/1)
|
||||
|> Enum.with_index()
|
||||
|> Enum.filter(&(elem(&1, 0) == :lt))
|
||||
|> Enum.map(&elem(&1, 1))
|
||||
|> Enum.map(&Kernel.+(&1, 1))
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
@divider_packets [[[2]], [[6]]]
|
||||
|
||||
def part2(pairs) do
|
||||
pairs
|
||||
|> Enum.flat_map(fn {p1, p2} -> [p1, p2] end)
|
||||
|> Kernel.++(@divider_packets)
|
||||
|> Enum.sort(Packet)
|
||||
|> Enum.with_index()
|
||||
|> Enum.filter(&(elem(&1, 0) in @divider_packets))
|
||||
|> Enum.map(&elem(&1, 1))
|
||||
|> Enum.map(&Kernel.+(&1, 1))
|
||||
|> Enum.reduce(&Kernel.*/2)
|
||||
end
|
||||
end
|
81
2022/lib/days/day16.ex
Normal file
81
2022/lib/days/day16.ex
Normal file
|
@ -0,0 +1,81 @@
|
|||
defmodule AOC.Day16 do
|
||||
use AOC.Day, day: 16, debug: true, input: "example"
|
||||
alias AOC.Day16.{ValveRegistry, Valve}
|
||||
|
||||
defmodule Valve do
|
||||
use GenServer
|
||||
|
||||
@impl true
|
||||
def init(state) do
|
||||
state = Map.put(state, :released, 0)
|
||||
state = Map.put(state, :open?, false)
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
def get_rate(valve) do
|
||||
GenServer.call(valve, :get_rate)
|
||||
end
|
||||
|
||||
def tick(valve) do
|
||||
GenServer.call(valve, :tick)
|
||||
end
|
||||
|
||||
def open(valve) do
|
||||
GenServer.call(valve, :open)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(:get_rate, _from, %{rate: rate} = state) do
|
||||
{:reply, rate, state}
|
||||
end
|
||||
|
||||
def handle_call(:tick, _from, %{open?: open?, released: released, rate: rate} = state) do
|
||||
released = if open?, do: released + rate, else: released
|
||||
{:reply, :ok, %{state | released: released}}
|
||||
end
|
||||
|
||||
def handle_call(:open, _from, state) do
|
||||
{:reply, :ok, %{state | open?: true}}
|
||||
end
|
||||
end
|
||||
|
||||
@line_regex ~r/Valve (?<name>[A-Z]{2}) has flow rate=(?<rate>\d+); tunnels? leads? to valves? (?<tunnels>.*)/
|
||||
|
||||
def parse_input(lines) do
|
||||
{:ok, _} = Registry.start_link(keys: :unique, name: ValveRegistry)
|
||||
|
||||
lines
|
||||
|> Enum.map(fn line ->
|
||||
%{"name" => name, "rate" => rate, "tunnels" => tunnels} =
|
||||
Regex.named_captures(@line_regex, line)
|
||||
|
||||
%{name: name, rate: String.to_integer(rate), tunnels: String.split(tunnels, ", ")}
|
||||
end)
|
||||
|> Enum.into(%{}, fn %{name: name} = valve ->
|
||||
{:ok, pid} =
|
||||
GenServer.start_link(Valve, valve, name: {:via, Registry, {ValveRegistry, name}})
|
||||
|
||||
{name, pid}
|
||||
end)
|
||||
end
|
||||
|
||||
def part1(valves) do
|
||||
Enum.map(valves, fn {name, valve} ->
|
||||
rate = Valve.get_rate(valve)
|
||||
{name, rate}
|
||||
end)
|
||||
|> Enum.sort_by(&elem(&1, 1), :desc)
|
||||
|> IO.inspect()
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def tick(valves) do
|
||||
Enum.each(valves, fn {_, valve} ->
|
||||
Valve.tick(valve)
|
||||
end)
|
||||
end
|
||||
|
||||
def part2(_input) do
|
||||
end
|
||||
end
|
68
2022/lib/days/day2.ex
Normal file
68
2022/lib/days/day2.ex
Normal file
|
@ -0,0 +1,68 @@
|
|||
defmodule AOC.Day2 do
|
||||
use AOC.Day, day: 2
|
||||
|
||||
def parse_input(lines) do
|
||||
Enum.map(lines, fn line ->
|
||||
[move1, move2] =
|
||||
line
|
||||
|> String.split(" ")
|
||||
|> Enum.map(fn c ->
|
||||
c
|
||||
|> String.to_charlist()
|
||||
|> hd()
|
||||
end)
|
||||
|
||||
{move1, move2}
|
||||
end)
|
||||
end
|
||||
|
||||
def part1(input) do
|
||||
input
|
||||
|> Enum.map(fn {move1, move2} ->
|
||||
score =
|
||||
case move2 do
|
||||
?X -> 1
|
||||
?Y -> 2
|
||||
?Z -> 3
|
||||
end
|
||||
|
||||
score +
|
||||
case {move1, move2} do
|
||||
{?A, ?Y} -> 6
|
||||
{?B, ?Z} -> 6
|
||||
{?C, ?X} -> 6
|
||||
{?A, ?X} -> 3
|
||||
{?B, ?Y} -> 3
|
||||
{?C, ?Z} -> 3
|
||||
_ -> 0
|
||||
end
|
||||
end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
def part2(input) do
|
||||
input
|
||||
|> Enum.map(fn {move1, result} ->
|
||||
score =
|
||||
case result do
|
||||
?X -> 0
|
||||
?Y -> 3
|
||||
?Z -> 6
|
||||
end
|
||||
|
||||
score +
|
||||
case {move1, result} do
|
||||
{?A, ?X} -> 3
|
||||
{?A, ?Y} -> 1
|
||||
{?A, ?Z} -> 2
|
||||
{?B, ?X} -> 1
|
||||
{?B, ?Y} -> 2
|
||||
{?B, ?Z} -> 3
|
||||
{?C, ?X} -> 2
|
||||
{?C, ?Y} -> 3
|
||||
{?C, ?Z} -> 1
|
||||
end
|
||||
end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
end
|
39
2022/lib/days/day3.ex
Normal file
39
2022/lib/days/day3.ex
Normal file
|
@ -0,0 +1,39 @@
|
|||
defmodule AOC.Day3 do
|
||||
use AOC.Day, day: 3
|
||||
|
||||
def parse_input(lines), do: lines
|
||||
|
||||
def part1(input) do
|
||||
input
|
||||
|> Enum.map(fn line ->
|
||||
comp_size = line |> String.length() |> div(2)
|
||||
{comp1, comp2} = line |> String.to_charlist() |> Enum.split(comp_size)
|
||||
comp1 = MapSet.new(comp1)
|
||||
comp2 = MapSet.new(comp2)
|
||||
|
||||
MapSet.intersection(comp1, comp2)
|
||||
|> MapSet.to_list()
|
||||
|> hd()
|
||||
|> get_priority()
|
||||
end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
def part2(input) do
|
||||
input
|
||||
|> Enum.chunk_every(3)
|
||||
|> Enum.map(fn group ->
|
||||
group
|
||||
|> Enum.map(&String.to_charlist/1)
|
||||
|> Enum.map(&MapSet.new/1)
|
||||
|> Enum.reduce(&MapSet.intersection/2)
|
||||
|> MapSet.to_list()
|
||||
|> hd()
|
||||
|> get_priority()
|
||||
end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
def get_priority(char) when char >= ?a, do: char - 96
|
||||
def get_priority(char), do: char - 38
|
||||
end
|
38
2022/lib/days/day4.ex
Normal file
38
2022/lib/days/day4.ex
Normal file
|
@ -0,0 +1,38 @@
|
|||
defmodule AOC.Day4 do
|
||||
use AOC.Day, day: 4
|
||||
|
||||
def parse_input(lines) do
|
||||
Enum.map(lines, fn line ->
|
||||
[assign1, assign2] =
|
||||
line
|
||||
|> String.split(",")
|
||||
|> Enum.map(fn assign ->
|
||||
[from, to] =
|
||||
assign
|
||||
|> String.split("-")
|
||||
|> Enum.map(&String.to_integer/1)
|
||||
|
||||
MapSet.new(from..to)
|
||||
end)
|
||||
|
||||
{assign1, assign2}
|
||||
end)
|
||||
end
|
||||
|
||||
def part1(input) do
|
||||
input
|
||||
|> Enum.map(fn {set1, set2} ->
|
||||
MapSet.subset?(set1, set2) or MapSet.subset?(set2, set1)
|
||||
end)
|
||||
|> Enum.count(& &1)
|
||||
end
|
||||
|
||||
def part2(input) do
|
||||
input
|
||||
|> Enum.map(fn {set1, set2} ->
|
||||
MapSet.intersection(set1, set2)
|
||||
|> Enum.any?()
|
||||
end)
|
||||
|> Enum.count(& &1)
|
||||
end
|
||||
end
|
70
2022/lib/days/day5.ex
Normal file
70
2022/lib/days/day5.ex
Normal file
|
@ -0,0 +1,70 @@
|
|||
defmodule AOC.Day5 do
|
||||
use AOC.Day, day: 5, trim: false
|
||||
|
||||
def parse_input(lines) do
|
||||
[init, ops] =
|
||||
lines
|
||||
|> Enum.chunk_by(&(&1 == "\n"))
|
||||
|> Enum.reject(&(&1 == ["\n"]))
|
||||
|
||||
{parse_init(init), parse_ops(ops)}
|
||||
end
|
||||
|
||||
def parse_init(init) do
|
||||
init
|
||||
|> Enum.split(-1)
|
||||
|> elem(0)
|
||||
|> Enum.map(fn line ->
|
||||
line
|
||||
|> String.to_charlist()
|
||||
|> tl()
|
||||
|> Enum.take_every(4)
|
||||
end)
|
||||
|> Enum.zip_with(&Function.identity/1)
|
||||
|> Enum.map(fn stack ->
|
||||
Enum.reject(stack, &(&1 == ?\s))
|
||||
end)
|
||||
end
|
||||
|
||||
def parse_ops(ops) do
|
||||
Enum.map(ops, fn op ->
|
||||
%{"count" => count, "from" => from, "to" => to} =
|
||||
Regex.named_captures(~r/move (?<count>\d+) from (?<from>\d+) to (?<to>\d+)\n/, op)
|
||||
|
||||
%{
|
||||
count: String.to_integer(count),
|
||||
from: String.to_integer(from) - 1,
|
||||
to: String.to_integer(to) - 1
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
def part1(input) do
|
||||
move(input, true)
|
||||
end
|
||||
|
||||
def part2(input) do
|
||||
move(input, false)
|
||||
end
|
||||
|
||||
def move({init, ops}, reverse) do
|
||||
ops
|
||||
|> Enum.reduce(init, fn %{count: count, from: from, to: to}, acc ->
|
||||
{acc, removed} = remove(acc, from, count)
|
||||
removed = if reverse, do: Enum.reverse(removed), else: removed
|
||||
add(acc, to, removed)
|
||||
end)
|
||||
|> Enum.map(&hd/1)
|
||||
end
|
||||
|
||||
def remove(stacks, from, count) do
|
||||
{removed, new} = Enum.split(Enum.at(stacks, from), count)
|
||||
stacks = List.update_at(stacks, from, fn _ -> new end)
|
||||
{stacks, removed}
|
||||
end
|
||||
|
||||
def add(stacks, to, removed) do
|
||||
added = Enum.concat(removed, Enum.at(stacks, to))
|
||||
List.update_at(stacks, to, fn _ -> added end)
|
||||
end
|
||||
end
|
29
2022/lib/days/day6.ex
Normal file
29
2022/lib/days/day6.ex
Normal file
|
@ -0,0 +1,29 @@
|
|||
defmodule AOC.Day6 do
|
||||
use AOC.Day, day: 6
|
||||
|
||||
def parse_input(lines), do: lines |> hd() |> String.to_charlist()
|
||||
|
||||
def part1(input) do
|
||||
find_marker(input, 0, 4)
|
||||
end
|
||||
|
||||
def part2(input) do
|
||||
find_marker(input, 0, 14)
|
||||
end
|
||||
|
||||
def find_marker(signal, index, length) do
|
||||
distinct =
|
||||
signal
|
||||
|> Enum.take(length)
|
||||
|> MapSet.new()
|
||||
|> Enum.count()
|
||||
|
||||
if distinct == length do
|
||||
index + length
|
||||
else
|
||||
signal
|
||||
|> tl()
|
||||
|> find_marker(index + 1, length)
|
||||
end
|
||||
end
|
||||
end
|
82
2022/lib/days/day7.ex
Normal file
82
2022/lib/days/day7.ex
Normal file
|
@ -0,0 +1,82 @@
|
|||
defmodule AOC.Day7 do
|
||||
use AOC.Day, day: 7
|
||||
|
||||
def parse_input(lines) do
|
||||
Enum.map(lines, fn
|
||||
"$ cd " <> arg ->
|
||||
{:cd, arg}
|
||||
|
||||
"$ ls" ->
|
||||
:ls
|
||||
|
||||
"dir " <> arg ->
|
||||
{:dir, arg}
|
||||
|
||||
line ->
|
||||
[size, file] = String.split(line)
|
||||
{:file, String.to_integer(size), file}
|
||||
end)
|
||||
end
|
||||
|
||||
def part1(input) do
|
||||
input
|
||||
|> get_file_sizes()
|
||||
|> get_directory_sizes()
|
||||
|> Enum.map(fn {_, size} -> size end)
|
||||
|> Enum.filter(&(&1 <= 100_000))
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
def part2(input) do
|
||||
file_sizes = get_file_sizes(input)
|
||||
|
||||
total_size =
|
||||
file_sizes
|
||||
|> Enum.map(&elem(&1, 1))
|
||||
|> Enum.sum()
|
||||
|
||||
size_needed = total_size - 40_000_000
|
||||
|
||||
file_sizes
|
||||
|> get_directory_sizes()
|
||||
|> Enum.map(&elem(&1, 1))
|
||||
|> Enum.filter(fn size -> size >= size_needed end)
|
||||
|> Enum.min()
|
||||
end
|
||||
|
||||
def get_file_sizes(input) do
|
||||
Enum.reduce(input, {[], []}, fn
|
||||
{:cd, "/"}, {_, sizes} ->
|
||||
{["/"], sizes}
|
||||
|
||||
{:cd, ".."}, {[_ | path], sizes} ->
|
||||
{path, sizes}
|
||||
|
||||
{:cd, dir}, {path, sizes} ->
|
||||
{[dir | path], sizes}
|
||||
|
||||
{:file, size, file}, {path, sizes} ->
|
||||
{path, [{[file | path], size} | sizes]}
|
||||
|
||||
_, acc ->
|
||||
acc
|
||||
end)
|
||||
|> elem(1)
|
||||
end
|
||||
|
||||
def get_directory_sizes(file_sizes) do
|
||||
file_sizes
|
||||
|> Enum.reduce(%{}, fn {[_ | path], size}, acc ->
|
||||
path = Enum.reverse(path)
|
||||
update_directory_size(acc, size, path)
|
||||
end)
|
||||
end
|
||||
|
||||
def update_directory_size(sizes, file_size, file_path) do
|
||||
Enum.reduce(file_path, {sizes, []}, fn dir, {sizes, cur_path} ->
|
||||
cur_path = [dir | cur_path]
|
||||
{Map.update(sizes, cur_path, file_size, &(&1 + file_size)), cur_path}
|
||||
end)
|
||||
|> elem(0)
|
||||
end
|
||||
end
|
53
2022/lib/days/day8.ex
Normal file
53
2022/lib/days/day8.ex
Normal file
|
@ -0,0 +1,53 @@
|
|||
defmodule AOC.Day8 do
|
||||
use AOC.Day, day: 8, debug: true
|
||||
|
||||
def parse_input(lines) do
|
||||
Enum.map(lines, fn line ->
|
||||
line
|
||||
|> String.split("", trim: true)
|
||||
|> Enum.map(&String.to_integer/1)
|
||||
end)
|
||||
end
|
||||
|
||||
def part1(rows) do
|
||||
rows = with_index(rows)
|
||||
|
||||
cols = Enum.zip(rows) |> Enum.map(&Tuple.to_list/1)
|
||||
rrows = Enum.map(rows, &Enum.reverse/1)
|
||||
rcols = Enum.map(cols, &Enum.reverse/1)
|
||||
|
||||
[rows, cols, rrows, rcols]
|
||||
|> Enum.map(&count_visible/1)
|
||||
|> Enum.reduce(&MapSet.union/2)
|
||||
|> Enum.count()
|
||||
end
|
||||
|
||||
def part2(rows) do
|
||||
rows
|
||||
end
|
||||
|
||||
def with_index(rows) do
|
||||
Enum.with_index(rows, fn row, y ->
|
||||
Enum.with_index(row, fn el, x ->
|
||||
%{height: el, x: x, y: y}
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
def count_visible(rows) do
|
||||
rows
|
||||
|> Enum.map(&count_visible_row/1)
|
||||
|> Enum.map(&elem(&1, 0))
|
||||
|> Enum.reduce(&MapSet.union/2)
|
||||
end
|
||||
|
||||
def count_visible_row(row) do
|
||||
Enum.reduce(row, {MapSet.new(), -1}, fn
|
||||
%{height: h, x: x, y: y}, {trees, highest} when h > highest ->
|
||||
{MapSet.put(trees, {x, y}), h}
|
||||
|
||||
_, acc ->
|
||||
acc
|
||||
end)
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue