change directory names

This commit is contained in:
Pim Kunis 2023-12-04 10:25:33 +01:00
parent 3cff0fc5cf
commit 78b8881016
90 changed files with 0 additions and 0 deletions

4
20/.formatter.exs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,3 @@
%{
"combination": {:hex, :combination, "0.0.3", "746aedca63d833293ec6e835aa1f34974868829b1486b1e1cb0685f0b2ae1f41", [:mix], [], "hexpm", "72b099f463df42ef7dc6371d250c7070b57b6c5902853f69deb894f79eda18ca"},
}

1
20/test/test_helper.exs Normal file
View file

@ -0,0 +1 @@
ExUnit.start()