Skip to content

Commit 51d6a41

Browse files
committed
Begin adding some heuristics for checking the graph
1 parent 82b9a95 commit 51d6a41

File tree

1 file changed

+29
-83
lines changed

1 file changed

+29
-83
lines changed

day24/src/day24.cr

+29-83
Original file line numberDiff line numberDiff line change
@@ -57,79 +57,40 @@ def solve(z3_src : String) : Array(Tuple(String, Int64))
5757
output_vars
5858
end
5959

60-
def swap(i : Int, j : Int, circuit : Circuit) : Circuit
61-
new_circuit = [*circuit]
62-
new_circuit[i] = {circuit[i][0], circuit[j][1]}
63-
new_circuit[j] = {circuit[j][0], circuit[i][1]}
64-
new_circuit
60+
def extract_int(prefix : String, values : Array(Tuple(String, Int64))) : Int
61+
values
62+
.select { |v| v[0].starts_with?(prefix) }
63+
.map { |v| v[1] }
64+
.reverse
65+
.reduce(0_i64) { |acc, b| (acc << 1) | b }
6566
end
6667

67-
def flip!(i : Int, vars : Vars)
68-
vars[i] = {vars[i][0], 1_i64 - vars[i][1]}
68+
def is_x(s : String) : Bool
69+
s.starts_with?("x")
6970
end
7071

71-
RNG = Random.new
72-
73-
def randomize!(vars : Vars)
74-
(0...vars.size).each do |i|
75-
if RNG.next_bool
76-
flip!(i, vars)
77-
end
78-
end
72+
def is_y(s : String) : Bool
73+
s.starts_with?("y")
7974
end
8075

81-
def find_best_swap(vars : Vars, circuit : Circuit, visited : Set(String)) : Tuple(Int32, Int32)
82-
output_vars = solve(translate_to_z3(vars, circuit))
83-
x = extract_int("x", output_vars)
84-
y = extract_int("y", output_vars)
85-
86-
smallest = 1000000
87-
smallest_i = -1
88-
smallest_j = -1
89-
(0...circuit.size)
90-
.flat_map do |i|
91-
# Baed on manual analysis of the GraphViz plot, the swaps seem to be relatively local
92-
({i + 1, circuit.size - 1}.min..{i + 5, circuit.size - 1}.min)
93-
.select { |j| i != j && !visited.includes?(circuit[i][1]) && !visited.includes?(circuit[j][1]) }
94-
.map { |j| {i, j} }
95-
end
96-
.each do |pair|
97-
i, j = pair
98-
max_d = 0
99-
max_d_z = 0
100-
vars = [*vars]
101-
4.times do
102-
randomize!(vars)
103-
output_vars = solve(translate_to_z3(vars, swap(i, j, circuit)))
104-
x = extract_int("x", output_vars)
105-
y = extract_int("y", output_vars)
106-
z = extract_int("z", output_vars)
107-
d = hamming_dist(x + y, z)
108-
if d > max_d
109-
max_d = d
110-
max_d_z = z
111-
end
112-
end
113-
if max_d < smallest
114-
puts "#{i}, #{j} -> #{max_d}"
115-
smallest = max_d
116-
smallest_i = i
117-
smallest_j = j
118-
end
119-
end
120-
{smallest_i, smallest_j}
76+
def is_z(s : String) : Bool
77+
s.starts_with?("z")
12178
end
12279

123-
def hamming_dist(n : Int64, m : Int64) : Int64
124-
(n ^ m).popcount
80+
def is_coord(s : String) : Bool
81+
is_x(s) || is_y(s) || is_z(s)
12582
end
12683

127-
def extract_int(prefix : String, values : Array(Tuple(String, Int64))) : Int
128-
values
129-
.select { |v| v[0].starts_with?(prefix) }
130-
.map { |v| v[1] }
131-
.reverse
132-
.reduce(0_i64) { |acc, b| (acc << 1) | b }
84+
def is_wrong(expr : Array(String), res : String) : Bool
85+
lhs, op, rhs = expr
86+
xor_gate = op == "XOR"
87+
or_gate = op == "OR"
88+
and_gate = op == "AND"
89+
90+
[
91+
xor_gate && !is_coord(lhs) && !is_coord(rhs) && !is_coord(res),
92+
!xor_gate && is_z(res) && res != "z45",
93+
].any?
13394
end
13495

13596
if ARGV.size == 0
@@ -152,24 +113,9 @@ else
152113
part1 = extract_int("z", solve(translate_to_z3(vars, circuit)))
153114
puts "Part 1: #{part1}"
154115

155-
# For part 2 we use a probabilistic algorithm that tries to find the best
156-
# swaps for randomized inputs.
157-
158-
swapped = [] of String
159-
visited = Set(String).new
160-
161-
4.times do |n|
162-
i, j = find_best_swap(vars, circuit, visited)
163-
164-
[circuit[i][1], circuit[j][1]].each do |v|
165-
swapped << v
166-
visited << v
167-
end
168-
169-
circuit = swap(i, j, circuit)
170-
171-
puts "#{i}, #{j}"
172-
end
173-
swapped.sort!
174-
puts "Part 2: #{swapped.join(",")}"
116+
part2 = circuit
117+
.select { |c| is_wrong(c[0], c[1]) }
118+
.map { |c| c[1] }
119+
.join(",")
120+
puts "Part 2: #{part2}"
175121
end

0 commit comments

Comments
 (0)