+4
-6
2025/day01.livemd
+4
-6
2025/day01.livemd
···
33
33
## Part 1
34
34
35
35
```elixir
36
-
Enum.reduce(instructions, {50, 0}, fn {_rot, val}, {curr, sum} ->
37
-
next = Integer.mod(curr + val, 100)
38
-
39
-
{next, sum + if(next == 0, do: 1, else: 0)}
36
+
Enum.scan(instructions, 50, fn {_rot, val}, curr ->
37
+
Integer.mod(curr + val, 100)
40
38
end)
41
-
|> elem(1)
39
+
|> Enum.count(& &1 == 0)
42
40
```
43
41
44
42
<!-- livebook:{"branch_parent_index":0} -->
···
66
64
|> elem(1)
67
65
```
68
66
69
-
<!-- livebook:{"offset":1324,"stamp":{"token":"XCP.pI2XE8mwBZJ1_5rOsosmoLh5-clSindOf0NN_piQhnU8r5baxfvJEHunCv7iXrvn3G43jfoHCFBZZK6KbxbIY0lzTJzKKWPrrl0bQ9S1RQvkk6dMv_LpnTv13HwNr3JYMw","version":2}} -->
67
+
<!-- livebook:{"offset":1270,"stamp":{"token":"XCP.fAfBqYepop325WQHLC6mJPz_Wpl7bDiz0qRbslvjG31mRJWcHALfstxgj_iOj3nf9_GCzh_WmPiNR4MnzZidF2bz_zMMXHTEo18hp1z3gQDdi5hTSO8UG4YSYnUEtGxO2g","version":2}} -->
+1
-1
2025/day07.livemd
+1
-1
2025/day07.livemd
···
114
114
|> Image.resize!(5, interpolate: :nearest)
115
115
```
116
116
117
-
<!-- livebook:{"offset":2553,"stamp":{"token":"XCP.jUh_Ehkij-SpgOuY0sdHr5YT6IaJif0yd6mre9jiQpu7HHlbAvVeyKXHCNfcp0L0eLTRgut7HlWmzGi3AlT1SFWsJqgDuD-ePZl8JBbLYfW9j0leDSmN7-aQgVCmlMvFDw","version":2}} -->
117
+
<!-- livebook:{"offset":2553,"stamp":{"token":"XCP.6lQNAY-yntHErm41fMi4xgwAxGSGZ7l6ekgYSe6b3Exm48kBKgyIoCK62LLVaeQP-1PfZFpd3tzHwAIA8OWTsnRBuWsxmex5qixMa-l4DaThm-jrSaVsE3Qj4aO-TrqdqA","version":2}} -->
+167
2025/day09.livemd
+167
2025/day09.livemd
···
1
+
# Day 09
2
+
3
+
```elixir
4
+
Mix.install([:kino_aoc, :image])
5
+
```
6
+
7
+
## Setup
8
+
9
+
<!-- livebook:{"attrs":"eyJhc3NpZ25fdG8iOiJwdXp6bGVfaW5wdXQiLCJkYXkiOiI5Iiwic2Vzc2lvbl9zZWNyZXQiOiJBRFZFTlRfT0ZfQ09ERV9TRVNTSU9OIiwieWVhciI6IjIwMjUifQ","chunks":null,"kind":"Elixir.KinoAOC.HelperCell","livebook_object":"smart_cell"} -->
10
+
11
+
```elixir
12
+
{:ok, puzzle_input} =
13
+
KinoAOC.download_puzzle("2025", "9", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION"))
14
+
```
15
+
16
+
```elixir
17
+
tiles =
18
+
puzzle_input
19
+
|> String.split()
20
+
|> Enum.map(fn raw ->
21
+
raw
22
+
|> String.split(",")
23
+
|> Enum.map(&String.to_integer/1)
24
+
|> List.to_tuple()
25
+
end)
26
+
```
27
+
28
+
## Implementation
29
+
30
+
```elixir
31
+
defmodule Combinatorics do
32
+
def combinations2(list) do
33
+
Stream.unfold(list, fn
34
+
[] -> nil
35
+
[x | rest] ->
36
+
curr = for y <- rest, do: [x, y]
37
+
38
+
{curr, rest}
39
+
end)
40
+
|> Stream.flat_map(& &1)
41
+
end
42
+
end
43
+
```
44
+
45
+
```elixir
46
+
defmodule Rect do
47
+
require Record
48
+
49
+
Record.defrecordp(:rect, l: 0, t: 0, r: 0, b: 0)
50
+
51
+
def new({ax, ay}, {bx, by}) do
52
+
rect(l: min(ax, bx), r: max(ax, bx), t: min(ay, by), b: max(ay, by))
53
+
end
54
+
55
+
def area(rect() = r) do
56
+
width(r) * height(r)
57
+
end
58
+
59
+
def intersect?(
60
+
rect(l: al, r: ar, t: at, b: ab),
61
+
rect(l: bl, r: br, t: bt, b: bb)
62
+
) do
63
+
al < br and ar > bl and at < bb and ab > bt
64
+
end
65
+
66
+
def width(rect(r: r, l: l)), do: r - l + 1
67
+
def height(rect(t: t, b: b)), do: b - t + 1
68
+
69
+
def to_svg(rect(l: x, t: y) = r, opts \\ []) do
70
+
~s"""
71
+
<rect x="#{x}" y="#{y}" width="#{width(r)}" height="#{height(r)}"
72
+
#{Enum.map_join(opts, " ", fn {k, v} -> ~s(#{k}="#{v}") end)} />
73
+
"""
74
+
end
75
+
end
76
+
```
77
+
78
+
```elixir
79
+
rects =
80
+
Combinatorics.combinations2(tiles)
81
+
|> Stream.map(fn [a, b] -> Rect.new(a, b) end)
82
+
|> Enum.sort()
83
+
```
84
+
85
+
<!-- livebook:{"branch_parent_index":1} -->
86
+
87
+
## Part 1
88
+
89
+
```elixir
90
+
rects
91
+
|> Enum.max_by(&Rect.area/1)
92
+
|> IO.inspect()
93
+
|> Rect.area()
94
+
```
95
+
96
+
<!-- livebook:{"branch_parent_index":1} -->
97
+
98
+
## Part 2
99
+
100
+
```elixir
101
+
edges =
102
+
tiles
103
+
|> Enum.chunk_every(2, 1, tiles)
104
+
|> Enum.map(&apply(Rect, :new, &1))
105
+
|> Enum.sort()
106
+
```
107
+
108
+
```elixir
109
+
# [{1916, 50285}, {94619, 50285}, {94619, 48466}, {1668, 48466}]
110
+
# |> Stream.flat_map(fn a ->
111
+
# for b <- tiles do
112
+
# Rect.new(a, b)
113
+
# end
114
+
# end)
115
+
rects
116
+
|> Enum.reduce({0, nil}, fn r, {max, p} ->
117
+
a = Rect.area(r)
118
+
119
+
if a > max and not Enum.any?(edges, &Rect.intersect?(r, &1)) do
120
+
{a, r}
121
+
else
122
+
{max, p}
123
+
end
124
+
end)
125
+
```
126
+
127
+
## Draw
128
+
129
+
```elixir
130
+
{{min_x, _}, {max_x, _}} = Enum.min_max(tiles)
131
+
```
132
+
133
+
```elixir
134
+
{{_, min_y}, {_, max_y}} = Enum.min_max_by(tiles, &elem(&1, 1))
135
+
```
136
+
137
+
```elixir
138
+
h = max_y + min_y
139
+
w = max_x + min_x
140
+
```
141
+
142
+
```elixir
143
+
{x, y} = hd(tiles)
144
+
145
+
p1 = {:rect, 16055, 14805, 85282, 83613}
146
+
p2 = {:rect, 5741, 50285, 94619, 67351}
147
+
148
+
svg = """
149
+
<svg xmlns="http://www.w3.org/2000/svg" width="500" viewBox="0 0 #{w} #{h}">
150
+
<rect width="100%" height="100%" fill="black" />
151
+
<path d="M#{x} #{y}#{
152
+
for {x, y} <- tl(tiles), do: "L#{x} #{y} "
153
+
} L#{x} #{y}" stroke="darkgreen" fill="green" stroke-width="200" />
154
+
#{Rect.to_svg(p1, stroke: :orange, fill: :transparent, "stroke-width": 200)}
155
+
#{Rect.to_svg(p2, stroke: :yellow, fill: :transparent, "stroke-width": 200)}
156
+
#{
157
+
for {x, y} <- tiles do
158
+
~s(<rect x="#{x - 100}" y="#{y - 100}" width="200" height="200" fill="red" />)
159
+
end
160
+
}
161
+
</svg>
162
+
"""
163
+
164
+
Kino.Image.new(svg, :svg)
165
+
```
166
+
167
+
<!-- livebook:{"offset":3329,"stamp":{"token":"XCP.SSlM8wg30CucU7IP0n8MTbPIvnvvcZRXcglo9DY17kk0O0fwtLfUUiYJauWdspkXUlp0Axl5YscQNBKK5mSycPLd0iNdz8JFPfjCg4rS2pyM3JuQj73ipXd27t8Yd0ylig","version":2}} -->
+188
2025/day10.livemd
+188
2025/day10.livemd
···
1
+
# Day 10
2
+
3
+
```elixir
4
+
Mix.install([:kino_aoc, {:dantzig, github: "hauleth/dantzig", ref: "use-proper-binary"}],
5
+
config: [
6
+
dantzig: [
7
+
highs_binary_path: System.find_executable("highs")
8
+
]
9
+
])
10
+
```
11
+
12
+
## Section
13
+
14
+
<!-- livebook:{"attrs":"eyJhc3NpZ25fdG8iOiJwdXp6bGVfaW5wdXQiLCJkYXkiOiIxMCIsInNlc3Npb25fc2VjcmV0IjoiQURWRU5UX09GX0NPREVfU0VTU0lPTiIsInllYXIiOiIyMDI1In0","chunks":null,"kind":"Elixir.KinoAOC.HelperCell","livebook_object":"smart_cell"} -->
15
+
16
+
```elixir
17
+
{:ok, puzzle_input} =
18
+
KinoAOC.download_puzzle("2025", "10", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION"))
19
+
```
20
+
21
+
```elixir
22
+
defmodule Decoder do
23
+
def decode("[" <> pattern), do: do_lights(String.reverse(String.trim_trailing(pattern, "]")), 0)
24
+
25
+
def decode("(" <> rest) do
26
+
<<seq::binary-size(byte_size(rest) - 1), ")">> = rest
27
+
28
+
seq
29
+
|> String.split(",")
30
+
|> Enum.map(&String.to_integer/1)
31
+
end
32
+
33
+
def decode("{" <> rest) do
34
+
<<seq::binary-size(byte_size(rest) - 1), "}">> = rest
35
+
36
+
seq
37
+
|> String.split(",")
38
+
|> Enum.map(&String.to_integer/1)
39
+
end
40
+
41
+
defp do_lights("", num), do: num
42
+
defp do_lights("." <> rest, num), do: do_lights(rest, 2 * num)
43
+
defp do_lights("#" <> rest, num), do: do_lights(rest, 2 * num + 1)
44
+
end
45
+
```
46
+
47
+
```elixir
48
+
indicators =
49
+
puzzle_input
50
+
|> String.split("\n", trim: true)
51
+
|> Enum.map(fn raw ->
52
+
[lights | rest] =
53
+
raw
54
+
|> String.split()
55
+
|> Enum.map(&Decoder.decode/1)
56
+
57
+
{buttons, [whatever]} = Enum.split(rest, -1)
58
+
59
+
{lights, buttons, whatever}
60
+
end)
61
+
```
62
+
63
+
<!-- livebook:{"branch_parent_index":0} -->
64
+
65
+
## Part 1
66
+
67
+
We are looking for such sequence of buttons that will provide pattern we are looking for. It can be solved by brute force with simple observation that buttons $a_n$ as well as light pattern $p$ can be represented by binary pattern. This allows us to use $\oplus$ (exclusive or, aka `XOR`) as an operation for pressing button.
68
+
69
+
Thanks to that observation we can see, that order in which we press buttons doesn't matter, as $\oplus$ is reflexive, i.e.:
70
+
71
+
$$
72
+
\begin{equation}
73
+
a \oplus b = b \oplus a
74
+
\end{equation}
75
+
$$
76
+
77
+
Additionally, wrt. $\oplus$ we have identity element $0$ and inverse element is the same as an original element, i.e.
78
+
79
+
$$
80
+
\begin{align}
81
+
a \oplus 0 = a \\
82
+
a \oplus a = 0
83
+
\end{align}
84
+
$$
85
+
86
+
Additionally we can observe that:
87
+
88
+
$$
89
+
\begin{equation}
90
+
(a \oplus b) \oplus c = a \oplus (b \oplus c)
91
+
\end{equation}
92
+
$$
93
+
94
+
With that observation we can deduce that each button will be pressed at most once and the order in which we press buttons doesn't matter.
95
+
96
+
---
97
+
98
+
So now we look for set $A = \{a_n\}$ of buttons that
99
+
100
+
$$
101
+
A \in \mathcal{P}(\text{Buttons}) \\
102
+
\forall_{i, j} \; i \ne j \implies a_i \ne a_j \\
103
+
\bigoplus A = p
104
+
$$
105
+
106
+
```elixir
107
+
defmodule Comb do
108
+
def all_possible([]), do: [[]]
109
+
def all_possible([a | rest]) do
110
+
sub = all_possible(rest)
111
+
112
+
sub ++ Enum.map(sub, &[a | &1])
113
+
end
114
+
end
115
+
```
116
+
117
+
```elixir
118
+
indicators
119
+
|> Enum.sum_by(fn {p, a, _} ->
120
+
a
121
+
|> Enum.map(&Enum.sum_by(&1, fn p -> 2 ** p end))
122
+
|> Comb.all_possible()
123
+
|> Enum.sort_by(&length/1)
124
+
|> Enum.find(fn seq ->
125
+
r = Enum.reduce(seq, 0, &Bitwise.bxor/2)
126
+
127
+
p == r
128
+
end)
129
+
|> length()
130
+
end)
131
+
```
132
+
133
+
<!-- livebook:{"branch_parent_index":0} -->
134
+
135
+
## Part 2
136
+
137
+
```elixir
138
+
defmodule Joltage do
139
+
alias Dantzig.Polynomial
140
+
alias Dantzig.Constraint
141
+
alias Dantzig.Problem
142
+
143
+
def solve({_pat, buttons, goal}) do
144
+
p = Problem.new(direction: :minimize)
145
+
146
+
{vars, {p, map}} =
147
+
buttons
148
+
|> Enum.with_index()
149
+
|> Enum.map_reduce({p, %{}}, fn {list, idx}, {p, acc} ->
150
+
{p, var} = Problem.new_variable(p, "v#{idx}", min: 0, type: :integer)
151
+
152
+
acc =
153
+
Enum.reduce(list, acc, fn key, map ->
154
+
Map.update(map, key, [var], &[var | &1])
155
+
end)
156
+
157
+
{var, {p, acc}}
158
+
end)
159
+
160
+
p =
161
+
map
162
+
|> Enum.sort()
163
+
|> Enum.map(&elem(&1, 1))
164
+
|> Enum.zip(goal)
165
+
|> Enum.reduce(p, fn {vars, target}, p ->
166
+
poly = Polynomial.sum(vars)
167
+
const = Constraint.new(poly, :==, target)
168
+
169
+
Problem.add_constraint(p, const)
170
+
end)
171
+
172
+
p = Problem.increment_objective(p, Polynomial.sum(vars))
173
+
174
+
{:ok, s} = Dantzig.HiGHS.solve(p)
175
+
176
+
Enum.sum_by(vars, &Dantzig.Solution.evaluate(s, &1))
177
+
end
178
+
end
179
+
```
180
+
181
+
```elixir
182
+
indicators
183
+
|> Task.async_stream(&Joltage.solve/1, ordered: false)
184
+
|> Enum.sum_by(&elem(&1, 1))
185
+
|> trunc()
186
+
```
187
+
188
+
<!-- livebook:{"offset":4336,"stamp":{"token":"XCP.sfoBGIHkFvWbEHVrY-dL-QiEcwRz_ZCIjn3lQ0RFHActIOwEapmOPBt0ygbQAmrnEjYPlFm5KrcWx4LfIPbSznxxcea0fYORG9GbBBpRCm-tYbGXYTCGgqgTvOsifyWDNg","version":2}} -->
+85
2025/day11.livemd
+85
2025/day11.livemd
···
1
+
# Day 11
2
+
3
+
```elixir
4
+
Mix.install([:kino_aoc])
5
+
```
6
+
7
+
## Parse
8
+
9
+
<!-- livebook:{"attrs":"eyJhc3NpZ25fdG8iOiJwdXp6bGVfaW5wdXQiLCJkYXkiOiIxMSIsInNlc3Npb25fc2VjcmV0IjoiQURWRU5UX09GX0NPREVfU0VTU0lPTiIsInllYXIiOiIyMDI1In0","chunks":null,"kind":"Elixir.KinoAOC.HelperCell","livebook_object":"smart_cell"} -->
10
+
11
+
```elixir
12
+
{:ok, puzzle_input} =
13
+
KinoAOC.download_puzzle("2025", "11", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION"))
14
+
```
15
+
16
+
```elixir
17
+
graph =
18
+
puzzle_input
19
+
|> String.split("\n", trim: true)
20
+
|> Map.new(fn <<from::binary-3>> <> ": " <> rest ->
21
+
{from, String.split(rest)}
22
+
end)
23
+
```
24
+
25
+
## Implementation
26
+
27
+
```elixir
28
+
defmodule Servers do
29
+
def paths(graph, from, to), do: paths(graph, from, to, [from])
30
+
31
+
defp paths(_graph, to, to, acc), do: [Enum.reverse(acc)]
32
+
33
+
defp paths(graph, from, to, acc) do
34
+
if next = graph[from] do
35
+
Stream.flat_map(next, &paths(graph, &1, to, [&1 | acc]))
36
+
else
37
+
[]
38
+
end
39
+
end
40
+
41
+
def paths_through(graph, from, to, required),
42
+
do: path_through(graph, from, to, MapSet.new(required), %{})
43
+
44
+
defp path_through(_graph, to, to, required, memo),
45
+
do: {if(Enum.empty?(required), do: 1, else: 0), memo}
46
+
47
+
defp path_through(graph, from, to, required, memo) do
48
+
state = MapSet.delete(required, from)
49
+
50
+
with :error <- Map.fetch(memo, {from, state}),
51
+
{:ok, next} <- Map.fetch(graph, from) do
52
+
{sum, memo} =
53
+
Enum.reduce(next, {0, memo}, fn n, {sum, acc} ->
54
+
{c, next_acc} = path_through(graph, n, to, state, acc)
55
+
56
+
{c + sum, next_acc}
57
+
end)
58
+
59
+
{sum, Map.put(memo, {from, state}, sum)}
60
+
else
61
+
:error -> {0, memo}
62
+
{:ok, val} -> {val, memo}
63
+
end
64
+
end
65
+
end
66
+
```
67
+
68
+
<!-- livebook:{"branch_parent_index":1} -->
69
+
70
+
## Part 1
71
+
72
+
```elixir
73
+
Servers.paths(graph, "you", "out") |> Enum.count()
74
+
```
75
+
76
+
<!-- livebook:{"branch_parent_index":1} -->
77
+
78
+
## Part 2
79
+
80
+
```elixir
81
+
Servers.paths_through(graph, "svr", "out", ["dac", "fft"])
82
+
|> elem(0)
83
+
```
84
+
85
+
<!-- livebook:{"offset":1933,"stamp":{"token":"XCP.fzzPot48c9xb6ftw8IEb1V6uTX6csBICnoXjN1PU6c3DylXjP-bco9PgawAoc2GSeNJRzS3NCPmJ9aO9Jm2ehPXnam5fN-bZvUbEcxKmNA8SqH2_fJ5o_qp8rIaxpH6DRQ","version":2}} -->
+73
2025/day12.livemd
+73
2025/day12.livemd
···
1
+
# Day 12
2
+
3
+
```elixir
4
+
Mix.install([:kino_aoc])
5
+
```
6
+
7
+
## Parsing
8
+
9
+
<!-- livebook:{"attrs":"eyJhc3NpZ25fdG8iOiJwdXp6bGVfaW5wdXQiLCJkYXkiOiIxMiIsInNlc3Npb25fc2VjcmV0IjoiQURWRU5UX09GX0NPREVfU0VTU0lPTiIsInllYXIiOiIyMDI1In0","chunks":null,"kind":"Elixir.KinoAOC.HelperCell","livebook_object":"smart_cell"} -->
10
+
11
+
```elixir
12
+
{:ok, puzzle_input} =
13
+
KinoAOC.download_puzzle("2025", "12", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION"))
14
+
```
15
+
16
+
```elixir
17
+
{areas, boxes} =
18
+
puzzle_input
19
+
|> String.split("\n\n")
20
+
|> List.pop_at(-1)
21
+
```
22
+
23
+
```elixir
24
+
areas =
25
+
areas
26
+
|> String.split("\n")
27
+
|> Enum.map(fn raw ->
28
+
[area | counts] = String.split(raw)
29
+
30
+
area =
31
+
area
32
+
|> String.trim(":")
33
+
|> String.split("x")
34
+
|> Enum.map(&String.to_integer/1)
35
+
|> Enum.product()
36
+
37
+
counts = Enum.map(counts, &String.to_integer/1)
38
+
39
+
{area, counts}
40
+
end)
41
+
```
42
+
43
+
```elixir
44
+
boxes =
45
+
boxes
46
+
|> Enum.map(fn <<_::binary-3>> <> rest ->
47
+
rest
48
+
|> String.to_charlist()
49
+
|> Enum.count(&(&1 == ?#))
50
+
end)
51
+
```
52
+
53
+
<!-- livebook:{"branch_parent_index":0} -->
54
+
55
+
## Part 1
56
+
57
+
```elixir
58
+
areas
59
+
|> Enum.count(fn {max, counts} ->
60
+
counts
61
+
|> Enum.zip_with(boxes, &*/2)
62
+
|> Enum.sum()
63
+
|> then(& &1 <= max)
64
+
end)
65
+
```
66
+
67
+
<!-- livebook:{"branch_parent_index":0} -->
68
+
69
+
## Part 2
70
+
71
+
FIN
72
+
73
+
<!-- livebook:{"offset":1266,"stamp":{"token":"XCP.VAO97d30rTEs0AWIHkPD4J0fLm3S60tQ3fKoA-riReFbzMnqL1jIoxttGhvNSnCfZVNfeUBuSYVe6PrIshxVGBwjr3pjNHCyFLSb4iSPNh277lkMmh6Gtrlfr8dvsYvw0g","version":2}} -->