mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 12:50:32 +01:00
Sort of functional implementation of 2024 day 24
This commit is contained in:
15
2024/bonus/24todot.py
Normal file
15
2024/bonus/24todot.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import fileinput
|
||||
|
||||
print("digraph day24 {")
|
||||
|
||||
for line in fileinput.input():
|
||||
parts = line.split(" ")
|
||||
if len(parts) != 5:
|
||||
continue
|
||||
|
||||
first, op, second, _, result = parts
|
||||
print(f'{first}{second}{op} [label="{op}"];')
|
||||
print(f"{first} -> {first}{second}{op} -> {result};")
|
||||
print(f"{second} -> {first}{second}{op};")
|
||||
|
||||
print("}")
|
||||
114
2024/src/aoc/days/day24.py
Normal file
114
2024/src/aoc/days/day24.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import functools
|
||||
import re
|
||||
|
||||
from . import SeparateRunner
|
||||
|
||||
|
||||
def parse_input(input: str) -> tuple[dict[str, int], dict[str, tuple[str, str, str]]]:
|
||||
variable_part, rules_part = input.strip().split("\n\n")
|
||||
|
||||
variables = {}
|
||||
|
||||
for line in variable_part.splitlines():
|
||||
variable, value = line.split(": ")
|
||||
variables[variable] = int(value)
|
||||
|
||||
rules = {}
|
||||
|
||||
for first, op, second, result in re.findall(
|
||||
r"(\w+) (XOR|OR|AND) (\w+) -> (\w+)", rules_part
|
||||
):
|
||||
rules[result] = (first, op, second)
|
||||
|
||||
return variables, rules
|
||||
|
||||
|
||||
class DayRunner(SeparateRunner):
|
||||
@classmethod
|
||||
def part1(cls, input: str) -> int:
|
||||
variables, rules = parse_input(input)
|
||||
|
||||
@functools.cache
|
||||
def get_value(variable: str) -> int:
|
||||
if variable in variables:
|
||||
return variables[variable]
|
||||
|
||||
first, op, second = rules[variable]
|
||||
first_v = get_value(first)
|
||||
second_v = get_value(second)
|
||||
|
||||
match op:
|
||||
case "AND":
|
||||
return first_v & second_v
|
||||
case "OR":
|
||||
return first_v | second_v
|
||||
case "XOR":
|
||||
return first_v ^ second_v
|
||||
|
||||
result = 0
|
||||
for variable in reversed(sorted(rules)):
|
||||
if not variable.startswith("z"):
|
||||
continue
|
||||
result = result * 2 + get_value(variable)
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def part2(cls, input: str) -> str:
|
||||
variables, rules = parse_input(input)
|
||||
|
||||
max_bit = int(
|
||||
max(variable for variable in rules if variable.startswith("z"))[1:]
|
||||
)
|
||||
|
||||
def find_invalid(output: str, pattern) -> set[str]:
|
||||
if pattern is None:
|
||||
return set()
|
||||
|
||||
if output in rules:
|
||||
left, op, right = rules[output]
|
||||
elif output == pattern:
|
||||
return set()
|
||||
else:
|
||||
return {output}
|
||||
|
||||
pop, pleft, pright = pattern
|
||||
|
||||
if op != pop:
|
||||
return {output}
|
||||
|
||||
wrong_normal = find_invalid(left, pleft) | find_invalid(right, pright)
|
||||
wrong_mirror = find_invalid(left, pright) | find_invalid(right, pleft)
|
||||
|
||||
least_wrong = min(wrong_mirror, wrong_normal, key=len)
|
||||
|
||||
return least_wrong
|
||||
|
||||
# First one is a half adder, that's a simple pattern
|
||||
invalid = find_invalid("z00", ["XOR", "x00", "y00"])
|
||||
# Second one is missing a reference to the before-previous adder, so it's a
|
||||
# slightly different patterns
|
||||
invalid |= find_invalid(
|
||||
"z01", ["XOR", ["AND", "x00", "y00"], ["XOR", "x01", "y01"]]
|
||||
)
|
||||
|
||||
for n in range(2, max_bit):
|
||||
xcurr = f"x{n:02}"
|
||||
ycurr = f"y{n:02}"
|
||||
zcurr = f"z{n:02}"
|
||||
xprev = f"x{n-1:02}"
|
||||
yprev = f"y{n-1:02}"
|
||||
|
||||
invalid |= find_invalid(
|
||||
zcurr,
|
||||
[
|
||||
"XOR",
|
||||
["XOR", xcurr, ycurr],
|
||||
["OR", ["AND", xprev, yprev], ["AND", ["XOR", xprev, yprev], None]],
|
||||
],
|
||||
)
|
||||
|
||||
# This code somehow believes `ktp` is invalid, but it's fine on closer
|
||||
# inspection. Will figure that out later.
|
||||
|
||||
return ",".join(sorted(invalid))
|
||||
47
2024/tests/samples/24.txt
Normal file
47
2024/tests/samples/24.txt
Normal file
@@ -0,0 +1,47 @@
|
||||
x00: 1
|
||||
x01: 0
|
||||
x02: 1
|
||||
x03: 1
|
||||
x04: 0
|
||||
y00: 1
|
||||
y01: 1
|
||||
y02: 1
|
||||
y03: 1
|
||||
y04: 1
|
||||
|
||||
ntg XOR fgs -> mjb
|
||||
y02 OR x01 -> tnw
|
||||
kwq OR kpj -> z05
|
||||
x00 OR x03 -> fst
|
||||
tgd XOR rvg -> z01
|
||||
vdt OR tnw -> bfw
|
||||
bfw AND frj -> z10
|
||||
ffh OR nrd -> bqk
|
||||
y00 AND y03 -> djm
|
||||
y03 OR y00 -> psh
|
||||
bqk OR frj -> z08
|
||||
tnw OR fst -> frj
|
||||
gnj AND tgd -> z11
|
||||
bfw XOR mjb -> z00
|
||||
x03 OR x00 -> vdt
|
||||
gnj AND wpb -> z02
|
||||
x04 AND y00 -> kjc
|
||||
djm OR pbm -> qhw
|
||||
nrd AND vdt -> hwm
|
||||
kjc AND fst -> rvg
|
||||
y04 OR y02 -> fgs
|
||||
y01 AND x02 -> pbm
|
||||
ntg OR kjc -> kwq
|
||||
psh XOR fgs -> tgd
|
||||
qhw XOR tgd -> z09
|
||||
pbm OR djm -> kpj
|
||||
x03 XOR y03 -> ffh
|
||||
x00 XOR y04 -> ntg
|
||||
bfw OR bqk -> z06
|
||||
nrd XOR fgs -> wpb
|
||||
frj XOR qhw -> z04
|
||||
bqk OR frj -> z07
|
||||
y03 OR x01 -> nrd
|
||||
hwm AND bqk -> z03
|
||||
tgd XOR rvg -> z12
|
||||
tnw OR pbm -> gnj
|
||||
7
2024/tests/test_day24.py
Normal file
7
2024/tests/test_day24.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from aoc.days.day24 import DayRunner
|
||||
|
||||
from . import get_data
|
||||
|
||||
|
||||
def test_sample_part1() -> None:
|
||||
assert DayRunner.part1(get_data(24)) == 2024
|
||||
Reference in New Issue
Block a user