1
0
Fork 0

Compare commits

..

5 commits

Author SHA1 Message Date
2c7cf0f86e
wip: 2015 day 24 2024-12-20 16:27:09 -05:00
4abffd3c5e
solve 2024 day 14 2024-12-20 16:26:28 -05:00
52db943b18
solve 2024 day 13 2024-12-14 12:05:54 -05:00
154e278fb3
solve 2024 day 12 2024-12-12 16:54:30 -05:00
1e677cbbc9
solve 2024 day 11 2024-12-12 08:47:49 -05:00
6 changed files with 380 additions and 3 deletions

View file

@ -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
View 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
View 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
View 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
View 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

View file

@ -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