Skip to content

Commit 90f58fc

Browse files
committed
2024 Day 24
1 parent 0c459a4 commit 90f58fc

File tree

2 files changed

+269
-0
lines changed

2 files changed

+269
-0
lines changed

2024/lib/day24.ex

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
defmodule Day24a do
2+
import Bitwise
3+
4+
def run do
5+
File.read!(".input.txt") |> run
6+
end
7+
8+
def run(input) do
9+
[init, prog] = input |> String.split("\n\n")
10+
11+
init =
12+
init
13+
|> String.split("\n")
14+
|> Enum.map(fn line -> line |> String.split(": ") |> List.to_tuple() end)
15+
|> Enum.map(fn {k, v} -> {k, String.to_integer(v)} end)
16+
|> Map.new()
17+
18+
{ops, graph} =
19+
prog
20+
|> String.split("\n")
21+
|> Enum.reduce({%{}, Graph.new()}, fn line, {ops, graph} ->
22+
[expr, res] = line |> String.split(" -> ")
23+
[lhs, op, rhs] = expr |> String.split(" ")
24+
25+
ops = ops |> Map.put(res, op)
26+
27+
graph =
28+
graph
29+
|> Graph.add_edges([{lhs, res}, {rhs, res}])
30+
31+
{ops, graph}
32+
end)
33+
34+
graph
35+
|> Graph.topsort()
36+
|> Enum.reduce(init, fn vert, nets ->
37+
if Map.has_key?(ops, vert) do
38+
[a, b] = Graph.in_neighbors(graph, vert) |> Enum.map(&nets[&1])
39+
op = ops[vert]
40+
41+
res =
42+
case op do
43+
"AND" -> band(a, b)
44+
"XOR" -> bxor(a, b)
45+
"OR" -> bor(a, b)
46+
end
47+
48+
Map.put(nets, vert, res)
49+
else
50+
nets
51+
end
52+
end)
53+
|> Enum.reduce(0, fn {k, v}, val ->
54+
if String.starts_with?(k, "z") do
55+
idx = k |> String.trim_leading("z") |> String.to_integer()
56+
57+
bor(val, bsl(v, idx))
58+
else
59+
val
60+
end
61+
end)
62+
end
63+
end
64+
65+
defmodule Day24b do
66+
import Bitwise
67+
68+
def run do
69+
File.read!(".input.txt") |> run
70+
end
71+
72+
def num(nets, init) do
73+
nets
74+
|> Enum.reduce(0, fn {k, v}, val ->
75+
if String.starts_with?(k, init) do
76+
idx = k |> String.trim_leading(init) |> String.to_integer()
77+
78+
bor(val, bsl(v, idx))
79+
else
80+
val
81+
end
82+
end)
83+
end
84+
85+
def run(input) do
86+
[_init, prog] = input |> String.split("\n\n")
87+
88+
swaps =
89+
%{
90+
"z07" => "rts",
91+
"rts" => "z07",
92+
"z12" => "jpj",
93+
"jpj" => "z12",
94+
"z26" => "kgj",
95+
"kgj" => "z26",
96+
"chv" => "vvw",
97+
"vvw" => "chv"
98+
}
99+
100+
s = fn x -> swaps[x] || x end
101+
102+
{ops, graph} =
103+
prog
104+
|> String.split("\n")
105+
|> Enum.reduce({%{}, Graph.new()}, fn line, {ops, graph} ->
106+
[expr, res] = line |> String.split(" -> ")
107+
[lhs, op, rhs] = expr |> String.split(" ")
108+
109+
ops = ops |> Map.put(s.(res), String.to_atom(op))
110+
111+
graph =
112+
graph
113+
|> Graph.add_edges([{lhs, s.(res)}, {rhs, s.(res)}])
114+
115+
{ops, graph}
116+
end)
117+
118+
1..44
119+
|> Enum.reduce("whb", fn n, c_in ->
120+
x = "x#{n |> to_string() |> String.pad_leading(2, "0")}"
121+
y = "y#{n |> to_string() |> String.pad_leading(2, "0")}"
122+
z = "z#{n |> to_string() |> String.pad_leading(2, "0")}"
123+
124+
xo = Graph.out_neighbors(graph, x)
125+
yo = Graph.out_neighbors(graph, y)
126+
127+
if MapSet.new(xo) != MapSet.new(yo) or length(xo) != 2 do
128+
IO.puts("bad first neighbors #{n}")
129+
end
130+
131+
xor1op = if ops[hd(xo)] == :XOR, do: hd(xo), else: hd(tl(xo))
132+
and1op = if ops[hd(xo)] == :XOR, do: hd(tl(xo)), else: hd(xo)
133+
134+
if ops[xor1op] != :XOR or ops[and1op] != :AND do
135+
IO.puts("bad op kind")
136+
end
137+
138+
xor1opo = Graph.out_neighbors(graph, xor1op)
139+
140+
if length(xor1opo) != 2 do
141+
IO.puts("bad xorop children: #{xor1op}")
142+
143+
orop? = Graph.out_neighbors(graph, and1op)
144+
145+
if length(orop?) != 1 do
146+
IO.puts("bad and1op children: #{orop?}")
147+
end
148+
149+
orop? |> Enum.filter(&(not String.starts_with?(&1, "z"))) |> hd()
150+
else
151+
xor2op = if ops[hd(xor1opo)] == :XOR, do: hd(xor1opo), else: hd(tl(xor1opo))
152+
and2op = if ops[hd(xor1opo)] == :XOR, do: hd(tl(xor1opo)), else: hd(xor1opo)
153+
154+
if xor2op != z do
155+
IO.puts("should be z: #{xor2op} (#{z})")
156+
end
157+
158+
if ops[xor2op] != :XOR or ops[and2op] != :AND do
159+
IO.puts("bad op2 kind")
160+
end
161+
162+
if Graph.out_neighbors(graph, c_in) |> Enum.find(&(&1 == and2op)) == nil do
163+
IO.puts("cin should direct to and2 #{c_in}")
164+
end
165+
166+
if Graph.out_neighbors(graph, c_in) |> Enum.find(&(&1 == xor2op)) == nil do
167+
IO.puts("cin should direct to xor2 #{c_in}")
168+
end
169+
170+
orop? =
171+
MapSet.intersection(
172+
MapSet.new(Graph.out_neighbors(graph, and1op)),
173+
MapSet.new(Graph.out_neighbors(graph, and2op))
174+
)
175+
|> MapSet.to_list()
176+
177+
if length(orop?) == 1 do
178+
[orop] = orop?
179+
180+
if ops[orop] != :OR do
181+
IO.puts("bad or op kind")
182+
end
183+
184+
orop
185+
else
186+
orop? =
187+
MapSet.union(
188+
MapSet.new(Graph.out_neighbors(graph, and1op)),
189+
MapSet.new(Graph.out_neighbors(graph, and2op))
190+
)
191+
|> MapSet.to_list()
192+
193+
[orop] = orop?
194+
195+
if ops[orop] != :OR do
196+
IO.puts("bad or op kind")
197+
end
198+
199+
orop
200+
end
201+
end
202+
end)
203+
204+
graph =
205+
ops
206+
|> Enum.reduce(graph, fn {vert, op}, graph ->
207+
graph |> Graph.label_vertex(vert, op)
208+
end)
209+
210+
dot = "digraph {\n"
211+
212+
dot =
213+
graph
214+
|> Graph.vertices()
215+
|> Enum.sort_by(fn vert ->
216+
cond do
217+
vert |> String.match?(~r/[xy][0-9][0-9]/) ->
218+
n = vert |> String.slice(1, 2) |> String.to_integer()
219+
{n, vert |> to_charlist() |> hd()}
220+
221+
vert |> String.match?(~r/z[0-9][0-9]/) ->
222+
n = vert |> String.slice(1, 2) |> String.to_integer()
223+
{n + 100, vert |> to_charlist() |> hd()}
224+
225+
true ->
226+
{1000, vert}
227+
end
228+
end)
229+
|> Enum.reduce(dot, fn vert, dot ->
230+
label = Graph.vertex_labels(graph, vert)
231+
232+
label =
233+
if label == [] do
234+
vert
235+
else
236+
[op] = label
237+
"#{op} -> #{vert}"
238+
end
239+
240+
dot <> "#{vert}[label=\"#{label}\"]\n"
241+
end)
242+
243+
dot =
244+
graph
245+
|> Graph.edges()
246+
|> Enum.reduce(dot, fn edge, dot ->
247+
dot <> "#{edge.v1} -> #{edge.v2}\n"
248+
end)
249+
250+
dot = dot <> "}\n"
251+
252+
File.write(".graph.dot", dot)
253+
254+
swaps |> Map.keys() |> Enum.sort() |> Enum.join(",") |> IO.puts()
255+
end
256+
end

2024/test/day24_test.exs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
defmodule Day24Test do
2+
use ExUnit.Case
3+
4+
def example, do: ""
5+
6+
test "Part 1 Example" do
7+
assert Day24a.run(example()) == :todo
8+
end
9+
10+
# test "Part 2 Example" do
11+
# assert Day24b.run(example()) == :todo
12+
# end
13+
end

0 commit comments

Comments
 (0)