Skip to content

Commit f7cea45

Browse files
committed
Solve 2025/10,11
1 parent b1d2cb8 commit f7cea45

5 files changed

Lines changed: 317 additions & 2 deletions

File tree

lib/2025/day_07.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule AdventOfCode.Y2025.Day07 do
77
"""
88
alias AdventOfCode.Helpers.{InputReader, Transformers}
99
alias Yog
10+
alias Yog.Traversal.{Sort, Walk}
1011

1112
def input, do: InputReader.read_from_file(2025, 7)
1213

@@ -32,7 +33,7 @@ defmodule AdventOfCode.Y2025.Day07 do
3233
start = find_start(grid)
3334

3435
reached =
35-
Yog.Traversal.Walk.fold_walk(
36+
Walk.fold_walk(
3637
over: graph,
3738
from: start,
3839
using: :breadth_first,
@@ -54,7 +55,7 @@ defmodule AdventOfCode.Y2025.Day07 do
5455
start = find_start(grid)
5556
max_r = get_max_r(grid)
5657

57-
case Yog.Traversal.Sort.topological_sort(graph) do
58+
case Sort.topological_sort(graph) do
5859
{:ok, sorted} ->
5960
counts =
6061
Enum.reduce(sorted, %{start => 1}, fn node, acc ->

lib/2025/day_10.ex

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
defmodule AdventOfCode.Y2025.Day10 do
2+
@moduledoc """
3+
--- Day 10: Factory ---
4+
Problem Link: https://adventofcode.com/2025/day/10
5+
Difficulty: l
6+
Tags: graph shortest-path bitwise dynamic-programming
7+
"""
8+
import Bitwise
9+
alias AdventOfCode.Helpers.{InputReader, Transformers}
10+
alias Yog.Pathfinding.AStar
11+
12+
def input, do: InputReader.read_from_file(2025, 10)
13+
14+
def run(input \\ input()) do
15+
input = parse(input)
16+
17+
{run_1(input), run_2(input)}
18+
end
19+
20+
def run_1(input) do
21+
input
22+
|> Enum.map(&solve_machine_1/1)
23+
|> Enum.sum()
24+
end
25+
26+
def run_2(input) do
27+
input
28+
|> Enum.map(fn m ->
29+
Process.put(:memo_p2, %{})
30+
res = solve_machine_2(m)
31+
Process.delete(:memo_p2)
32+
res
33+
end)
34+
|> Enum.sum()
35+
end
36+
37+
defp solve_machine_1(m) do
38+
states = get_button_states(m.buttons_lists)
39+
target_mask = m.indicator
40+
41+
states
42+
|> Map.get(target_mask, [])
43+
|> Enum.map(fn {_, presses} -> presses end)
44+
|> case do
45+
[] ->
46+
search_machine_1_yog(m)
47+
48+
list ->
49+
Enum.min(list)
50+
end
51+
end
52+
53+
defp search_machine_1_yog(%{indicator: target_mask, buttons_mask: buttons_mask}) do
54+
successors = fn mask ->
55+
Enum.map(buttons_mask, fn btn -> {bxor(mask, btn), 1} end)
56+
end
57+
58+
heuristic = fn mask ->
59+
diff = bxor(mask, target_mask)
60+
count_set_bits(diff)
61+
end
62+
63+
is_goal = fn mask -> mask == target_mask end
64+
65+
case AStar.implicit_a_star(0, successors, is_goal, heuristic) do
66+
{:ok, cost} -> cost
67+
:error -> 0
68+
end
69+
end
70+
71+
defp count_set_bits(v) do
72+
if v == 0 do
73+
0
74+
else
75+
1 + count_set_bits(v &&& v - 1)
76+
end
77+
end
78+
79+
defp solve_machine_2(%{joltage: target_v, buttons_lists: buttons_lists}) do
80+
states = get_button_states(buttons_lists)
81+
solve_recursive(target_v, states)
82+
end
83+
84+
defp solve_recursive(v, states) do
85+
if Enum.all?(v, &(&1 == 0)) do
86+
0
87+
else
88+
memo = Process.get(:memo_p2)
89+
90+
if Map.has_key?(memo, v) do
91+
Map.get(memo, v)
92+
else
93+
res = compute_recursive(v, states)
94+
Process.put(:memo_p2, Map.put(memo, v, res))
95+
res
96+
end
97+
end
98+
end
99+
100+
defp compute_recursive(v, states) do
101+
target_parity =
102+
v
103+
|> Enum.with_index()
104+
|> Enum.reduce(0, fn {val, idx}, acc ->
105+
if rem(val, 2) == 1, do: acc + (1 <<< idx), else: acc
106+
end)
107+
108+
subsets = Map.get(states, target_parity, [])
109+
110+
subsets
111+
|> Enum.flat_map(fn {inc_v, presses} ->
112+
next_v =
113+
Enum.zip_with(v, inc_v, fn v_val, inc_val ->
114+
v_val - inc_val
115+
end)
116+
117+
if Enum.all?(next_v, &(&1 >= 0)) do
118+
next_v_div = Enum.map(next_v, &div(&1, 2))
119+
[presses + 2 * solve_recursive(next_v_div, states)]
120+
else
121+
[]
122+
end
123+
end)
124+
|> case do
125+
# Infinity
126+
[] -> 1_000_000_000
127+
list -> Enum.min(list)
128+
end
129+
end
130+
131+
defp get_button_states(buttons_lists) do
132+
m = length(buttons_lists)
133+
134+
num_counters =
135+
if buttons_lists == [] do
136+
0
137+
else
138+
buttons_lists |> List.flatten() |> Enum.max() |> Kernel.+(1)
139+
end
140+
141+
0..((1 <<< m) - 1)
142+
|> Enum.reduce(%{}, fn i, acc ->
143+
{parity, inc_v, presses} =
144+
Enum.reduce(0..(m - 1), {0, List.duplicate(0, num_counters), 0}, fn j, {p, iv, pr} ->
145+
if (i &&& 1 <<< j) != 0 do
146+
btn = Enum.at(buttons_lists, j)
147+
# Update parity mask
148+
p2 = Enum.reduce(btn, p, fn bit, p_acc -> bxor(p_acc, 1 <<< bit) end)
149+
# Update increment vector
150+
iv2 =
151+
Enum.reduce(btn, iv, fn bit, iv_acc -> List.update_at(iv_acc, bit, &(&1 + 1)) end)
152+
153+
{p2, iv2, pr + 1}
154+
else
155+
{p, iv, pr}
156+
end
157+
end)
158+
159+
Map.update(acc, parity, [{inc_v, presses}], &[{inc_v, presses} | &1])
160+
end)
161+
end
162+
163+
def parse(data \\ input()) do
164+
data
165+
|> Transformers.lines()
166+
|> Enum.map(&parse_line/1)
167+
end
168+
169+
defp parse_line(line) do
170+
[[_, indicator_str]] = Regex.scan(~r/\[([.#]+)\]/, line)
171+
buttons_matches = Regex.scan(~r/\(([\d,]+)\)/, line)
172+
[[_, joltage_str]] = Regex.scan(~r/\{([\d,]+)\}/, line)
173+
174+
indicator_list =
175+
indicator_str
176+
|> String.graphemes()
177+
|> Enum.map(fn
178+
"." -> 0
179+
"#" -> 1
180+
end)
181+
182+
indicator_mask =
183+
indicator_list
184+
|> Enum.with_index()
185+
|> Enum.reduce(0, fn {val, idx}, acc ->
186+
if val == 1, do: acc + (1 <<< idx), else: acc
187+
end)
188+
189+
buttons_lists =
190+
buttons_matches
191+
|> Enum.map(fn [_, m] ->
192+
m |> String.split(",") |> Enum.map(&String.to_integer/1)
193+
end)
194+
195+
buttons_mask =
196+
buttons_lists
197+
|> Enum.map(fn indices ->
198+
Enum.reduce(indices, 0, fn idx, acc -> acc + (1 <<< idx) end)
199+
end)
200+
201+
joltage =
202+
joltage_str
203+
|> String.split(",")
204+
|> Enum.map(&String.to_integer/1)
205+
206+
%{
207+
num_lights: length(indicator_list),
208+
indicator: indicator_mask,
209+
buttons_lists: buttons_lists,
210+
buttons_mask: buttons_mask,
211+
joltage: joltage
212+
}
213+
end
214+
end

lib/2025/day_11.ex

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
defmodule AdventOfCode.Y2025.Day11 do
2+
@moduledoc """
3+
--- Day 11: Reactor ---
4+
Problem Link: https://adventofcode.com/2025/day/11
5+
Difficulty: m
6+
Tags: graph dynamic-programming
7+
"""
8+
alias AdventOfCode.Helpers.{InputReader, Transformers}
9+
10+
def input, do: InputReader.read_from_file(2025, 11)
11+
12+
def run(input \\ input()) do
13+
graph = parse(input)
14+
15+
{run_1(graph), run_2(graph)}
16+
end
17+
18+
def run_1(graph) do
19+
do_count_paths(graph, "you", "out")
20+
end
21+
22+
def run_2(graph) do
23+
p1 =
24+
do_count_paths(graph, "svr", "dac") *
25+
do_count_paths(graph, "dac", "fft") *
26+
do_count_paths(graph, "fft", "out")
27+
28+
p2 =
29+
do_count_paths(graph, "svr", "fft") *
30+
do_count_paths(graph, "fft", "dac") *
31+
do_count_paths(graph, "dac", "out")
32+
33+
p1 + p2
34+
end
35+
36+
defp do_count_paths(_graph, source, source), do: 1
37+
38+
defp do_count_paths(graph, source, target) do
39+
if Yog.Traversal.reachable?(graph, source, target) do
40+
{:ok, order} = Yog.Traversal.topological_sort(graph)
41+
42+
order
43+
|> Enum.reverse()
44+
|> Enum.reduce(%{}, fn node, acc ->
45+
if node == target do
46+
Map.put(acc, node, 1)
47+
else
48+
graph
49+
|> Yog.successor_ids(node)
50+
|> Enum.reduce(0, fn succ, sum ->
51+
sum + Map.get(acc, succ, 0)
52+
end)
53+
|> then(&Map.put(acc, node, &1))
54+
end
55+
end)
56+
|> Map.get(source, 0)
57+
else
58+
0
59+
end
60+
end
61+
62+
def parse(data \\ input()) do
63+
data
64+
|> Transformers.lines()
65+
|> Enum.reduce(Yog.directed(), fn line, g ->
66+
[src, dsts_str] = String.split(line, ":", parts: 2)
67+
68+
dsts_str
69+
|> String.trim()
70+
|> String.split(" ", trim: true)
71+
|> Enum.reduce(g, fn dst, g_acc ->
72+
Yog.add_edge_ensure(g_acc, String.trim(src), dst, 1, nil)
73+
end)
74+
end)
75+
end
76+
end

test/2025/day_10_test.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
defmodule AdventOfCode.Y2025.Day10Test do
2+
@moduledoc false
3+
4+
use ExUnit.Case, async: true
5+
@moduletag :y2510
6+
7+
alias AdventOfCode.Y2025.Day10, as: Solution
8+
9+
test "Year 2025, Day 10 run/1" do
10+
assert Solution.run() == {375, 15_377}
11+
end
12+
end

test/2025/day_11_test.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
defmodule AdventOfCode.Y2025.Day11Test do
2+
@moduledoc false
3+
4+
use ExUnit.Case, async: true
5+
@moduletag :y2511
6+
7+
alias AdventOfCode.Y2025.Day11, as: Solution
8+
9+
test "Year 2025, Day 11 run/1" do
10+
assert Solution.run() == {428, 331_468_292_364_745}
11+
end
12+
end

0 commit comments

Comments
 (0)