Compare commits
5 commits
7daff40cc3
...
2c7cf0f86e
Author | SHA1 | Date | |
---|---|---|---|
2c7cf0f86e | |||
4abffd3c5e | |||
52db943b18 | |||
154e278fb3 | |||
1e677cbbc9 |
6 changed files with 380 additions and 3 deletions
|
@ -3,7 +3,7 @@
|
|||
| S | M | T | W | T | F | S |
|
||||
| :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|
||||
| [1] | [2] | [3] | [4] | [5] | [6] | [7] |
|
||||
| [8] | [9] | [10]| 11 | 12 | 13 | 14 |
|
||||
| [8] | [9] | [10]| [11]| [12]| [13]| [14]|
|
||||
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
|
||||
| 22 | 23 | 24 | 25 | | | |
|
||||
|
||||
|
@ -17,3 +17,7 @@
|
|||
[8]: ./lib/2024/8.ex
|
||||
[9]: ./lib/2024/9.ex
|
||||
[10]: ./lib/2024/10.ex
|
||||
[11]: ./lib/2024/11.ex
|
||||
[12]: ./lib/2024/12.ex
|
||||
[13]: ./lib/2024/13.ex
|
||||
[14]: ./lib/2024/14.ex
|
||||
|
|
89
2024/lib/2024/11.ex
Normal file
89
2024/lib/2024/11.ex
Normal file
|
@ -0,0 +1,89 @@
|
|||
import AOC
|
||||
import AOC.Prelude
|
||||
|
||||
aoc 2024, 11 do
|
||||
require Integer
|
||||
|
||||
def p1(input) do
|
||||
input
|
||||
|> read_stones()
|
||||
|> count_stones_after_blink(25)
|
||||
end
|
||||
|
||||
def p2(input) do
|
||||
input
|
||||
|> read_stones()
|
||||
|> count_stones_after_blink(75)
|
||||
end
|
||||
|
||||
##
|
||||
|
||||
defp count_stones_after_blink(stones, n) do
|
||||
stones
|
||||
|> Stream.iterate(&blink/1)
|
||||
|> Enum.at(n)
|
||||
|> count_stones()
|
||||
end
|
||||
|
||||
defp blink(stones) do
|
||||
for {stone, count} <- stones, reduce: stones do
|
||||
stones ->
|
||||
cond do
|
||||
stone == 0 ->
|
||||
stones
|
||||
|> remove_stones(0, count)
|
||||
|> add_stones(1, count)
|
||||
|
||||
even_digits?(stone) ->
|
||||
{left, right} = split_stone(stone)
|
||||
|
||||
stones
|
||||
|> remove_stones(stone, count)
|
||||
|> add_stones(left, count)
|
||||
|> add_stones(right, count)
|
||||
|
||||
true ->
|
||||
stones
|
||||
|> remove_stones(stone, count)
|
||||
|> add_stones(stone * 2024, count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp even_digits?(num) do
|
||||
num
|
||||
|> Integer.digits()
|
||||
|> Enum.count()
|
||||
|> Integer.is_even()
|
||||
end
|
||||
|
||||
defp split_stone(num) do
|
||||
digits = Integer.digits(num)
|
||||
|
||||
{left, right} = Enum.split(digits, div(length(digits), 2))
|
||||
|
||||
{Integer.undigits(left), Integer.undigits(right)}
|
||||
end
|
||||
|
||||
defp remove_stones(stones, num, count) do
|
||||
Map.update(stones, num, 0, &(&1 - count))
|
||||
end
|
||||
|
||||
defp add_stones(stones, num, count) do
|
||||
Map.update(stones, num, count, &(&1 + count))
|
||||
end
|
||||
|
||||
defp count_stones(stones) do
|
||||
stones
|
||||
|> Enum.map(&elem(&1, 1))
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
## input
|
||||
|
||||
defp read_stones(input) do
|
||||
input
|
||||
|> ints()
|
||||
|> Enum.frequencies()
|
||||
end
|
||||
end
|
112
2024/lib/2024/12.ex
Normal file
112
2024/lib/2024/12.ex
Normal file
|
@ -0,0 +1,112 @@
|
|||
import AOC
|
||||
import AOC.Prelude
|
||||
|
||||
aoc 2024, 12 do
|
||||
def p1(input) do
|
||||
input
|
||||
|> map_grid()
|
||||
|> group_plots()
|
||||
|> Enum.map(&size_plot/1)
|
||||
|> Enum.map(fn {area, perimeter, _} -> area * perimeter end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
def p2(input) do
|
||||
input
|
||||
|> map_grid()
|
||||
|> group_plots()
|
||||
|> Enum.map(&size_plot/1)
|
||||
|> Enum.map(fn {area, _, sides} -> area * sides end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
## group plots
|
||||
|
||||
defp group_plots(map, plots \\ [])
|
||||
defp group_plots(map, plots) when map_size(map) == 0, do: plots
|
||||
|
||||
defp group_plots(map, plots) do
|
||||
{pos, plant} = Enum.at(map, 0)
|
||||
|
||||
plot = walk_plot(map, pos, plant)
|
||||
|
||||
{_, map} = Map.split(map, MapSet.to_list(plot))
|
||||
|
||||
group_plots(map, [plot | plots])
|
||||
end
|
||||
|
||||
defp size_plot(plot) do
|
||||
area = MapSet.size(plot)
|
||||
|
||||
perimeter =
|
||||
for pos <- plot, reduce: 0 do
|
||||
sum ->
|
||||
sum +
|
||||
([&n/1, &s/1, &e/1, &w/1]
|
||||
|> Enum.map(& &1.(pos))
|
||||
|> Enum.count(&(not MapSet.member?(plot, &1))))
|
||||
end
|
||||
|
||||
{area, perimeter, count_sides(plot)}
|
||||
end
|
||||
|
||||
## traversal
|
||||
|
||||
defp walk_plot(map, pos, plant, plot \\ MapSet.new()) do
|
||||
plot = MapSet.put(plot, pos)
|
||||
|
||||
[&n/1, &s/1, &e/1, &w/1]
|
||||
|> Enum.map(& &1.(pos))
|
||||
|> Enum.reject(&MapSet.member?(plot, &1))
|
||||
|> Enum.filter(&(Map.get(map, &1) == plant))
|
||||
|> Enum.reduce(plot, fn pos, plot ->
|
||||
walk_plot(map, pos, plant, plot)
|
||||
end)
|
||||
end
|
||||
|
||||
def n({x, y}), do: {x, y + 1}
|
||||
def s({x, y}), do: {x, y - 1}
|
||||
def e({x, y}), do: {x + 1, y}
|
||||
def w({x, y}), do: {x - 1, y}
|
||||
|
||||
defp count_sides(plot) do
|
||||
# find exterior faces
|
||||
exterior_faces =
|
||||
for pos <- plot, dir <- ~w[n e s w]a, exterior?(plot, pos, dir) do
|
||||
{pos, dir}
|
||||
end
|
||||
|
||||
exterior_faces
|
||||
# group exterior faces
|
||||
|> Enum.group_by(
|
||||
fn
|
||||
{{_, y}, dir} when dir in ~w[n s]a -> {dir, y}
|
||||
{{x, _}, dir} when dir in ~w[e w]a -> {dir, x}
|
||||
end,
|
||||
fn
|
||||
{{x, _}, dir} when dir in ~w[n s]a -> x
|
||||
{{_, y}, dir} when dir in ~w[e w]a -> y
|
||||
end
|
||||
)
|
||||
|> Map.values()
|
||||
# count groups of exterior faces, splitting non-monotonic sections
|
||||
|> Enum.map(fn group ->
|
||||
{faces, _} =
|
||||
group
|
||||
|> Enum.sort()
|
||||
|> Enum.reduce({0, nil}, fn
|
||||
n, {sides, nil} -> {sides + 1, n}
|
||||
n, {sides, last} when last + 1 == n -> {sides, n}
|
||||
n, {sides, _last} -> {sides + 1, n}
|
||||
end)
|
||||
|
||||
faces
|
||||
end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
defp exterior?(plot, pos, dir) do
|
||||
neighboring_pos = apply(__MODULE__, dir, [pos])
|
||||
not MapSet.member?(plot, neighboring_pos)
|
||||
end
|
||||
end
|
76
2024/lib/2024/13.ex
Normal file
76
2024/lib/2024/13.ex
Normal file
|
@ -0,0 +1,76 @@
|
|||
import AOC
|
||||
|
||||
aoc 2024, 13 do
|
||||
def p1(input) do
|
||||
input
|
||||
|> read_machines()
|
||||
|> Enum.map(&solve/1)
|
||||
|> Enum.filter(&match?({:solution, a, b} when a <= 100 and b <= 100, &1))
|
||||
|> Enum.map(&tokens_for_solution/1)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
def p2(input) do
|
||||
input
|
||||
|> read_machines()
|
||||
|> Enum.map(fn %{goal: {x, y}} = machine ->
|
||||
%{machine | goal: {x + 10_000_000_000_000, y + 10_000_000_000_000}}
|
||||
end)
|
||||
|> Enum.map(&solve/1)
|
||||
|> Enum.map(&tokens_for_solution/1)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
defp solve(%{a: {ax, ay}, b: {bx, by}, goal: {x, y}}) do
|
||||
# a * ax + b * bx = x
|
||||
# a * ay + b * by = y
|
||||
|
||||
# a * ax + b * bx = x
|
||||
# a * ax = x - b * bx
|
||||
# a = (x - b * bx) / ax
|
||||
|
||||
# a * ay + b * by = y
|
||||
# ((x - b * bx) / ax) * ay + b * by = y
|
||||
# ((ay * (x - b * bx)) / ax) + b * by = y
|
||||
# ((ay * x - ay * b * bx) / ax) + b * by = y
|
||||
# ay * x - ay * b * bx + b * by * ax = y * ax
|
||||
# b * by * ax - b * ay * bx = y * ax - x * ay
|
||||
# b * (by * ax - ay * bx) = y * ax - x * ay
|
||||
# b = (y * ax - x * ay) / (by * ax - ay * bx)
|
||||
|
||||
b = div(y * ax - x * ay, by * ax - ay * bx)
|
||||
a = div(x - b * bx, ax)
|
||||
|
||||
if a * ax + b * bx == x and a * ay + b * by == y do
|
||||
{:solution, a, b}
|
||||
else
|
||||
:impossible
|
||||
end
|
||||
end
|
||||
|
||||
defp tokens_for_solution({:solution, a, b}) do
|
||||
3 * a + b
|
||||
end
|
||||
|
||||
defp tokens_for_solution(_), do: 0
|
||||
|
||||
## input
|
||||
|
||||
defp read_machines(str) do
|
||||
~r/Button A: X\+(\d+), Y\+(\d+)\nButton B: X\+(\d+), Y\+(\d+)\nPrize: X=(\d+), Y=(\d+)/m
|
||||
|> Regex.scan(
|
||||
str,
|
||||
capture: :all_but_first
|
||||
)
|
||||
|> Enum.map(fn strings ->
|
||||
[xa, ya, xb, yb, x, y] =
|
||||
Enum.map(strings, &String.to_integer/1)
|
||||
|
||||
%{
|
||||
a: {xa, ya},
|
||||
b: {xb, yb},
|
||||
goal: {x, y}
|
||||
}
|
||||
end)
|
||||
end
|
||||
end
|
96
2024/lib/2024/14.ex
Normal file
96
2024/lib/2024/14.ex
Normal file
|
@ -0,0 +1,96 @@
|
|||
import AOC
|
||||
|
||||
aoc 2024, 14 do
|
||||
# @bounds {11, 7}
|
||||
@bounds {101, 103}
|
||||
|
||||
def p1(input) do
|
||||
input
|
||||
|> read_robots()
|
||||
|> Stream.iterate(fn robots ->
|
||||
Enum.map(robots, &move_robot(&1, @bounds))
|
||||
end)
|
||||
|> Enum.at(100)
|
||||
|> Enum.map(&elem(&1, 0))
|
||||
|> Enum.frequencies_by(&quadrant(&1, @bounds))
|
||||
|> Map.delete(-1)
|
||||
|> Map.values()
|
||||
|> Enum.product()
|
||||
end
|
||||
|
||||
def p2(input) do
|
||||
input
|
||||
|> read_robots()
|
||||
|> Stream.iterate(fn robots ->
|
||||
Enum.map(robots, &move_robot(&1, @bounds))
|
||||
end)
|
||||
|> Stream.with_index()
|
||||
# noticed convergence every 101 seconds
|
||||
|> Stream.take_every(101)
|
||||
|> Enum.each(fn {robots, n} ->
|
||||
IO.puts("")
|
||||
IO.puts("------------------ Seconds elapsed: #{n} ------------------")
|
||||
IO.puts("")
|
||||
|
||||
robots
|
||||
|> Enum.frequencies_by(&elem(&1, 0))
|
||||
|> render(@bounds)
|
||||
|
||||
IO.gets("Continue?")
|
||||
end)
|
||||
end
|
||||
|
||||
defp move_robot({{x, y}, {vx, vy} = velocity}, {max_x, max_y}) do
|
||||
x_next = wrap_value(x + vx, max_x)
|
||||
y_next = wrap_value(y + vy, max_y)
|
||||
|
||||
{{x_next, y_next}, velocity}
|
||||
end
|
||||
|
||||
defp wrap_value(v, max) do
|
||||
if v < 0 do
|
||||
max + v
|
||||
else
|
||||
rem(v, max)
|
||||
end
|
||||
end
|
||||
|
||||
defp quadrant({x, y}, {max_x, max_y}) do
|
||||
half_x = div(max_x, 2)
|
||||
half_y = div(max_y, 2)
|
||||
|
||||
case {x < half_x, y < half_y, half_x == x or half_y == y} do
|
||||
# special grouping on the lines bisecting that plane
|
||||
{_, _, true} -> -1
|
||||
# bin into quadrants
|
||||
{true, true, _} -> 0
|
||||
{false, true, _} -> 1
|
||||
{true, false, _} -> 2
|
||||
{false, false, _} -> 3
|
||||
end
|
||||
end
|
||||
|
||||
defp render(robots, {max_x, max_y}) do
|
||||
for y <- 0..max_y do
|
||||
line =
|
||||
for x <- 0..max_x, into: "" do
|
||||
"#{Map.get(robots, {x, y}, " ")}"
|
||||
end
|
||||
|
||||
IO.puts(line)
|
||||
end
|
||||
|
||||
robots
|
||||
end
|
||||
|
||||
## input
|
||||
|
||||
defp read_robots(input) do
|
||||
~r/p=(\d+),(\d+) v=(-?\d+),(-?\d+)/
|
||||
|> Regex.scan(input, capture: :all_but_first)
|
||||
|> Enum.map(fn nums ->
|
||||
[px, py, vx, vy] = Enum.map(nums, &String.to_integer/1)
|
||||
{{px, py}, {vx, vy}}
|
||||
end)
|
||||
end
|
||||
end
|
|
@ -15,8 +15,8 @@
|
|||
| [2021] | **43/50** 🌟 | Elixir |
|
||||
| [2022] | **14/50** 🌟 | Elixir, Haskell |
|
||||
| [2023] | **19/50** 🌟 | Elixir, Haskell |
|
||||
| [2024] | **20/50** 🌟 | Elixir |
|
||||
| **Total** | **176** 🌟| |
|
||||
| [2024] | **28/50** 🌟 | Elixir |
|
||||
| **Total** | **184** 🌟| |
|
||||
|
||||
[2015]: ./2015
|
||||
[2017]: ./2017
|
||||
|
|
Loading…
Reference in a new issue