Skip to content

Commit 5ce48df

Browse files
committed
chore: wip on 2024 day 24b
1 parent 2eeb7fe commit 5ce48df

File tree

1 file changed

+38
-155
lines changed

1 file changed

+38
-155
lines changed

2024/24b.py

+38-155
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,7 @@
11
from aoc import *
2-
from itertools import pairwise, combinations, repeat, product, accumulate, permutations, cycle, combinations_with_replacement
3-
from more_itertools import first_true, flatten, ncycles, first_true, zip_broadcast, windowed, chunked, take, peekable
42
import re
5-
from blessed import BlessedList
6-
from collections import namedtuple, defaultdict, OrderedDict, Counter, deque, UserList
7-
import operator
8-
from operator import itemgetter
9-
from bisect import *
10-
from functools import reduce, cache, cmp_to_key
11-
import math
12-
from copy import deepcopy
13-
from math import ceil
14-
from heapq import *
15-
import sys
16-
from pprint import pprint as pp
17-
from statistics import mode
18-
from string import ascii_uppercase
19-
import random
3+
from more_itertools import flatten
204

21-
def incorrect_z(s: int, n: int) -> [str]:
22-
i = 0
23-
incorrect = []
24-
while s or n:
25-
a = s % 2
26-
b = n % 2
27-
s //= 2
28-
n //= 2
29-
if a != b:
30-
incorrect.append(f'z{i:02}')
31-
i += 1
32-
return incorrect
33-
34-
def first_incorrect_z(s: int, n: int) -> str:
35-
i = 0
36-
while s or n:
37-
a = s % 2
38-
b = n % 2
39-
s //= 2
40-
n //= 2
41-
if a != b:
42-
return f'z{i:02}'
43-
i += 1
44-
45-
def random_number() -> int:
46-
return random.randrange(2**44, 2**45)
47-
return random.randrange(2**45)
48-
49-
def find_all_deps(dependencies, wire, current_set):
50-
if dependencies[wire][0][0] not in 'xy':
51-
current_set.add(dependencies[wire][0])
52-
find_all_deps(dependencies, dependencies[wire][0], current_set)
53-
if dependencies[wire][1][0] not in 'xy':
54-
current_set.add(dependencies[wire][1])
55-
find_all_deps(dependencies, dependencies[wire][1], current_set)
56-
57-
def to_values(num: int, letter: str, values: dict):
58-
for i in range(45):
59-
a = num % 2
60-
num //= 2
61-
values[f'{letter}{a:02}'] = bool(a)
62-
63-
64-
def compute(x: int, y: int, swap) -> int:
65-
values = {}
66-
to_values(x, 'x', values)
67-
to_values(y, 'y', values)
68-
changed = True
69-
while changed:
70-
changed = False
71-
for a, oper, b, c in rul:
72-
# print(a, oper, b, c)
73-
a = swap[a]
74-
b = swap[b]
75-
c = swap[c]
76-
if c not in values and a in values and b in values:
77-
if oper == 'OR':
78-
values[c] = values[a] or values[b]
79-
elif oper == 'AND':
80-
values[c] = values[a] and values[b]
81-
elif oper == 'XOR':
82-
values[c] = values[a] ^ values[b]
83-
else:
84-
print('BAD')
85-
changed = True
86-
# print(rul)
87-
# print(values)
88-
ooo = sorted(((k, c) for k, c in values.items() if k[0] == 'z'), key=lambda x: x[0], reverse=True)
89-
# print(ooo)
90-
return int(''.join(str(int(c)) for _, c in ooo), base=2)
91-
92-
def first_bad(swap):
93-
x = random_number()
94-
y = random_number()
95-
n = x+y
96-
computed = compute(x, y, swap)
97-
if n == computed:
98-
return None
99-
return first_incorrect_z(n, computed)
1005

1016
def main(infi: str):
1027
inp = filerstrip(infi)
@@ -105,70 +10,48 @@ def main(infi: str):
10510
for i in inputs.split('\n'):
10611
name, val = i.split(': ')
10712
values[name] = bool(int(val))
108-
# print(values)
109-
global rul
11013
rul = []
111-
# print(rules)
112-
dependencies = {}
113-
possible_outputs = set()
11414
for r in rules.split('\n'):
115-
# print(r)
11615
match = re.fullmatch(r'^(.*?) (.*?) (.*?) -> (.*?)$', r)
11716
rul.append((match[1], match[2], match[3], match[4]))
118-
dependencies[match[4]] = (match[1], match[3])
119-
possible_outputs.add(match[4])
120-
possible_outputs.add(match[1])
121-
possible_outputs.add(match[3])
122-
basic_swapper = {k: k for k in possible_outputs}
123-
# print([(k, c) for k, c in values.items() if k[0] == 'z'])
124-
# print(sorted(((k, c) for k, c in values.items() if k[0] == 'z'), key=lambda x: x[0], reverse=True))
125-
# ooo = sorted(((k, c) for k, c in values.items() if k[0] == 'z'), key=lambda x: x[0], reverse=True)
126-
# aaa = sorted(((k, c) for k, c in values.items() if k[0] == 'x'), key=lambda x: x[0], reverse=True)
127-
# bbb = sorted(((k, c) for k, c in values.items() if k[0] == 'y'), key=lambda x: x[0], reverse=True)
128-
# number = int(''.join(str(int(c)) for _, c in ooo), base=2)
129-
# x = int(''.join(str(int(c)) for _, c in aaa), base=2)
130-
# y = int(''.join(str(int(c)) for _, c in bbb), base=2)
131-
# print(bin(x+y))
132-
# print(bin(number))
133-
134-
# deps = incorrect_z(x+y, number)
135-
# print(deps)
136-
# sets = []
137-
# for dep in deps:
138-
# dep_set = set()
139-
# find_all_deps(dependencies, dep, dep_set)
140-
# # print(dep_set)
141-
# sets.append(dep_set)
142-
# print(reduce(operator.and_, sets))
143-
# return
144-
# [find_all_deps(dependencies, dep, dep_set) for dep in deps]
145-
# comb = list(combinations(dep_set, 2))
146-
# # for c in combinations(comb, 4):
147-
# # if len(set(flatten(c))) != 8:
148-
# # continue
149-
# # print(c)
150-
# print(dep_set)
151-
152-
active_swapper = basic_swapper.copy()
153-
while True:
154-
first_incorrect = first_bad(active_swapper)
155-
print('at', first_incorrect)
156-
if first_incorrect is None:
157-
print(active_swapper)
158-
break
159-
dep_set = set()
160-
find_all_deps(dependencies, first_incorrect, dep_set)
161-
for a, b in combinations(dep_set, 2):
162-
new_swapper = active_swapper.copy()
163-
new_swapper[a] = b
164-
new_swapper[b] = a
165-
if any(int(first_bad(new_swapper)[1:]) <= int(first_incorrect[1:]) for i in range(100)):
166-
continue
167-
else:
168-
active_swapper = new_swapper
169-
break
17+
input_ands = {
18+
d
19+
for x, oper, y, d in rul
20+
if oper == 'AND' and x[0] in 'xy' and y[0] in 'xy'
21+
}
22+
other_ands = {d for x, oper, y, d in rul if oper == 'AND'} - input_ands
23+
carry_inputs = set(
24+
flatten([(x, y) for x, oper, y, d in rul if oper == 'OR'])
25+
)
26+
ands = {d for x, oper, y, d in rul if oper == 'AND'}
27+
# Here is my notation of an adder:
28+
# x XOR y = a c XOR a = s
29+
# x AND y = d a AND c = b d OR b = c_out
30+
# By manually inspecting XORs of input values (x.. and y.., grep 'XOR .* ->
31+
# z' 24.txt | sort -k 5), we can see that z05, z09 and z30 are missing. By
32+
# inspecting other XOR operations (those that don't output z.., grep 'XOR .*
33+
# -> [^z]' 24.txt | sort), we can see that nbf, hdt and gbf are outputs
34+
# XORed fom inputs other than x.. and y...
35+
#
36+
# The last two mismatched outputs needed are computed here. We take output
37+
# from AND operations and compare them with inputs of ORs. Those that don't
38+
# match are misplaced. jfb is a special case since that one is used to
39+
# compute carry for the LSB and isn't merged with previous carry, so it's
40+
# correct.
41+
print(
42+
','.join(
43+
sorted(
44+
list(
45+
(
46+
ands ^ carry_inputs
47+
)
48+
- {'jfb'}
49+
)
50+
+ ['z05', 'hdt']
51+
)
52+
)
53+
)
17054

171-
return
17255

17356
DAY = 24
17457
FILE_TEST = f"{DAY}_testa.txt"

0 commit comments

Comments
 (0)