···11+import fileinput
22+from collections import defaultdict
33+44+from z3 import Int, Optimize
55+66+77+def solve(goal, toggles, part_2=False):
88+ s = Optimize()
99+1010+ # Create z3 variables for each "light" (light/counter).
1111+ lights = [Int('j' + str(i)) for i, g in enumerate(goal)]
1212+1313+ # Create z3 variables to count each button press.
1414+ buttons = [Int('t' + str(i)) for i in range(len(toggles))]
1515+1616+ # Count instances of indicator light appearing in toggle.
1717+ appearances = defaultdict(list)
1818+ for i, toggle in enumerate(toggles):
1919+ for t in toggle:
2020+ appearances[t].append(i)
2121+2222+ # Add constraints for `counter == sum of buttons pressed`.
2323+ for i, ts in appearances.items():
2424+ s.add(lights[int(i)] == sum(buttons[x] for x in ts))
2525+2626+ # Enforce non-negative numbers of button presses.
2727+ for b in buttons:
2828+ s.add(b >= 0)
2929+3030+ # Add constraints depending on if we're solving part 1 or 2.
3131+ for i, g in enumerate(goal):
3232+ if part_2:
3333+ s.add(lights[i] == g)
3434+ else:
3535+ # We just care about parity in part 1.
3636+ s.add(lights[i] % 2 == g)
3737+3838+ # Minimize the total number of button presses.
3939+ s.minimize(sum(buttons))
4040+4141+ # Answer is the sum of button presses from the solver.
4242+ s.check()
4343+ m = s.model()
4444+ return sum(m[b].as_long() for b in buttons)
4545+4646+4747+part_1 = 0
4848+part_2 = 0
4949+for line in fileinput.input():
5050+ parts = line.strip().split()
5151+ lights, *toggles, joltage = parts
5252+5353+ toggles = [t[1:-1].split(',') for t in toggles]
5454+ p1_goal = [1 if c == '#' else 0 for c in lights[1:-1]]
5555+ p2_goal = [int(x) for x in joltage[1:-1].split(',')]
5656+5757+ part_1 += solve(p1_goal, toggles)
5858+ part_2 += solve(p2_goal, toggles, part_2=True)
5959+6060+print("Part 1:", part_1)
6161+print("Part 2:", part_2)