Finally finished the puzzle (aoc day 7 2015); I firmly believe that the circuit can do a whole lot more than what is described in the puzzle text

This commit is contained in:
2023-11-29 15:08:10 +01:00
parent 1884a9075d
commit 8e277d86f5
4 changed files with 686 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
[package]
name = "main"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom = "7.1.3"
petgraph = "0.6.4"

View File

@@ -0,0 +1,339 @@
NOT dq -> dr
kg OR kf -> kh
ep OR eo -> eq
44430 -> b
NOT gs -> gt
dd OR do -> dp
eg AND ei -> ej
y AND ae -> ag
jx AND jz -> ka
lf RSHIFT 2 -> lg
z AND aa -> ac
dy AND ej -> el
bj OR bi -> bk
kk RSHIFT 3 -> km
NOT cn -> co
gn AND gp -> gq
cq AND cs -> ct
eo LSHIFT 15 -> es
lg OR lm -> ln
dy OR ej -> ek
NOT di -> dj
1 AND fi -> fj
kf LSHIFT 15 -> kj
NOT jy -> jz
NOT ft -> fu
fs AND fu -> fv
NOT hr -> hs
ck OR cl -> cm
jp RSHIFT 5 -> js
iv OR jb -> jc
is OR it -> iu
ld OR le -> lf
NOT fc -> fd
NOT dm -> dn
bn OR by -> bz
aj AND al -> am
cd LSHIFT 15 -> ch
jp AND ka -> kc
ci OR ct -> cu
gv AND gx -> gy
de AND dk -> dm
x RSHIFT 5 -> aa
et RSHIFT 2 -> eu
x RSHIFT 1 -> aq
ia OR ig -> ih
bk LSHIFT 1 -> ce
y OR ae -> af
NOT ca -> cb
e AND f -> h
ia AND ig -> ii
ck AND cl -> cn
NOT jh -> ji
z OR aa -> ab
1 AND en -> eo
ib AND ic -> ie
NOT eh -> ei
iy AND ja -> jb
NOT bb -> bc
ha OR gz -> hb
1 AND cx -> cy
NOT ax -> ay
ev OR ew -> ex
bn RSHIFT 2 -> bo
er OR es -> et
eu OR fa -> fb
jp OR ka -> kb
ea AND eb -> ed
k AND m -> n
et RSHIFT 3 -> ev
et RSHIFT 5 -> ew
hz RSHIFT 1 -> is
ki OR kj -> kk
NOT h -> i
lv LSHIFT 15 -> lz
as RSHIFT 1 -> bl
hu LSHIFT 15 -> hy
iw AND ix -> iz
lf RSHIFT 1 -> ly
fp OR fv -> fw
1 AND am -> an
ap LSHIFT 1 -> bj
u LSHIFT 1 -> ao
b RSHIFT 5 -> f
jq AND jw -> jy
iu RSHIFT 3 -> iw
ih AND ij -> ik
NOT iz -> ja
de OR dk -> dl
iu OR jf -> jg
as AND bd -> bf
b RSHIFT 3 -> e
jq OR jw -> jx
iv AND jb -> jd
cg OR ch -> ci
iu AND jf -> jh
lx -> a
1 AND cc -> cd
ly OR lz -> ma
NOT el -> em
1 AND bh -> bi
fb AND fd -> fe
lf OR lq -> lr
bn RSHIFT 3 -> bp
bn AND by -> ca
af AND ah -> ai
cf LSHIFT 1 -> cz
dw OR dx -> dy
gj AND gu -> gw
jg AND ji -> jj
jr OR js -> jt
bl OR bm -> bn
gj RSHIFT 2 -> gk
cj OR cp -> cq
gj OR gu -> gv
b OR n -> o
o AND q -> r
bi LSHIFT 15 -> bm
dy RSHIFT 1 -> er
cu AND cw -> cx
iw OR ix -> iy
hc OR hd -> he
0 -> c
db OR dc -> dd
kk RSHIFT 2 -> kl
eq LSHIFT 1 -> fk
dz OR ef -> eg
NOT ed -> ee
lw OR lv -> lx
fw AND fy -> fz
dz AND ef -> eh
jp RSHIFT 3 -> jr
lg AND lm -> lo
ci RSHIFT 2 -> cj
be AND bg -> bh
lc LSHIFT 1 -> lw
hm AND ho -> hp
jr AND js -> ju
1 AND io -> ip
cm AND co -> cp
ib OR ic -> id
NOT bf -> bg
fo RSHIFT 5 -> fr
ip LSHIFT 15 -> it
jt AND jv -> jw
jc AND je -> jf
du OR dt -> dv
NOT fx -> fy
aw AND ay -> az
ge LSHIFT 15 -> gi
NOT ak -> al
fm OR fn -> fo
ff AND fh -> fi
ci RSHIFT 5 -> cl
cz OR cy -> da
NOT ey -> ez
NOT ju -> jv
NOT ls -> lt
kk AND kv -> kx
NOT ii -> ij
kl AND kr -> kt
jk LSHIFT 15 -> jo
e OR f -> g
NOT bs -> bt
hi AND hk -> hl
hz OR ik -> il
ek AND em -> en
ao OR an -> ap
dv LSHIFT 1 -> ep
an LSHIFT 15 -> ar
fo RSHIFT 1 -> gh
NOT im -> in
kk RSHIFT 1 -> ld
hw LSHIFT 1 -> iq
ec AND ee -> ef
hb LSHIFT 1 -> hv
kb AND kd -> ke
x AND ai -> ak
dd AND do -> dq
aq OR ar -> as
iq OR ip -> ir
dl AND dn -> do
iu RSHIFT 5 -> ix
as OR bd -> be
NOT go -> gp
fk OR fj -> fl
jm LSHIFT 1 -> kg
NOT cv -> cw
dp AND dr -> ds
dt LSHIFT 15 -> dx
et RSHIFT 1 -> fm
dy RSHIFT 3 -> ea
fp AND fv -> fx
NOT p -> q
dd RSHIFT 2 -> de
eu AND fa -> fc
ba AND bc -> bd
dh AND dj -> dk
lr AND lt -> lu
he RSHIFT 1 -> hx
ex AND ez -> fa
df OR dg -> dh
fj LSHIFT 15 -> fn
NOT kx -> ky
gk OR gq -> gr
dy RSHIFT 2 -> dz
gh OR gi -> gj
lj AND ll -> lm
x OR ai -> aj
bz AND cb -> cc
1 AND lu -> lv
as RSHIFT 3 -> au
ce OR cd -> cf
il AND in -> io
dd RSHIFT 1 -> dw
NOT lo -> lp
c LSHIFT 1 -> t
dd RSHIFT 3 -> df
dd RSHIFT 5 -> dg
lh AND li -> lk
lf RSHIFT 5 -> li
dy RSHIFT 5 -> eb
NOT kt -> ku
at OR az -> ba
x RSHIFT 3 -> z
NOT lk -> ll
lb OR la -> lc
1 AND r -> s
lh OR li -> lj
ln AND lp -> lq
kk RSHIFT 5 -> kn
ea OR eb -> ec
ci AND ct -> cv
b RSHIFT 2 -> d
jp RSHIFT 1 -> ki
NOT cr -> cs
NOT jd -> je
jp RSHIFT 2 -> jq
jn OR jo -> jp
lf RSHIFT 3 -> lh
1 AND ds -> dt
lf AND lq -> ls
la LSHIFT 15 -> le
NOT fg -> fh
at AND az -> bb
au AND av -> ax
kw AND ky -> kz
v OR w -> x
kk OR kv -> kw
ks AND ku -> kv
kh LSHIFT 1 -> lb
1 AND kz -> la
NOT kc -> kd
x RSHIFT 2 -> y
et OR fe -> ff
et AND fe -> fg
NOT ac -> ad
jl OR jk -> jm
1 AND jj -> jk
bn RSHIFT 1 -> cg
NOT kp -> kq
ci RSHIFT 3 -> ck
ev AND ew -> ey
1 AND ke -> kf
cj AND cp -> cr
ir LSHIFT 1 -> jl
NOT gw -> gx
as RSHIFT 2 -> at
iu RSHIFT 1 -> jn
cy LSHIFT 15 -> dc
hg OR hh -> hi
ci RSHIFT 1 -> db
au OR av -> aw
km AND kn -> kp
gj RSHIFT 1 -> hc
iu RSHIFT 2 -> iv
ab AND ad -> ae
da LSHIFT 1 -> du
NOT bw -> bx
km OR kn -> ko
ko AND kq -> kr
bv AND bx -> by
kl OR kr -> ks
1 AND ht -> hu
df AND dg -> di
NOT ag -> ah
d OR j -> k
d AND j -> l
b AND n -> p
gf OR ge -> gg
gg LSHIFT 1 -> ha
bn RSHIFT 5 -> bq
bo OR bu -> bv
1 AND gy -> gz
s LSHIFT 15 -> w
NOT ie -> if
as RSHIFT 5 -> av
bo AND bu -> bw
hz AND ik -> im
bp AND bq -> bs
b RSHIFT 1 -> v
NOT l -> m
bp OR bq -> br
g AND i -> j
br AND bt -> bu
t OR s -> u
hz RSHIFT 5 -> ic
gk AND gq -> gs
fl LSHIFT 1 -> gf
he RSHIFT 3 -> hg
gz LSHIFT 15 -> hd
hf OR hl -> hm
1 AND gd -> ge
fo OR fz -> ga
id AND if -> ig
fo AND fz -> gb
gr AND gt -> gu
he OR hp -> hq
fq AND fr -> ft
ga AND gc -> gd
fo RSHIFT 2 -> fp
gl OR gm -> gn
hg AND hh -> hj
NOT hn -> ho
gl AND gm -> go
he RSHIFT 5 -> hh
NOT gb -> gc
hq AND hs -> ht
hz RSHIFT 3 -> ib
hz RSHIFT 2 -> ia
fq OR fr -> fs
hx OR hy -> hz
he AND hp -> hr
gj RSHIFT 5 -> gm
hf AND hl -> hn
hv OR hu -> hw
NOT hj -> hk
gj RSHIFT 3 -> gl
fo RSHIFT 3 -> fq
he RSHIFT 2 -> hf

View File

@@ -0,0 +1,329 @@
use core::fmt;
use std::{collections::HashMap, iter::zip};
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{self, alpha1, multispace1},
multi::separated_list1,
sequence::tuple,
IResult, Parser,
};
#[derive(Debug, Clone)]
enum Instruction {
Assign(u16, String),
Connect(String, String),
Not(String, String),
And(String, String, String),
OneAnd(String, String),
Or(String, String, String),
Lshift(u16, String, String),
Rshift(u16, String, String),
}
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Instruction::Assign(value, wire) => write!(f, "{} -> {}", value, wire),
Instruction::Connect(source, target) => write!(f, "{} -> {}", source, target),
Instruction::Not(source, target) => write!(f, "NOT {} -> {}", source, target),
Instruction::And(source1, source2, target) => {
write!(f, "{} AND {} -> {}", source1, source2, target)
}
Instruction::OneAnd(source, target) => write!(f, "1 AND {} -> {}", source, target),
Instruction::Or(source1, source2, target) => {
write!(f, "{} OR {} -> {}", source1, source2, target)
}
Instruction::Lshift(value, source, target) => {
write!(f, "{} LSHIFT {} -> {}", source, value, target)
}
Instruction::Rshift(value, source, target) => {
write!(f, "{} RSHIFT {} -> {}", source, value, target)
}
}
}
}
#[derive(Debug, Clone, Copy)]
enum Port {
Noop,
Not,
And,
Or,
Lshift(u16),
Rshift(u16),
}
impl fmt::Display for Port {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Port::Noop => write!(f, "Noop"),
Port::Not => write!(f, "Not"),
Port::And => write!(f, "And"),
Port::Or => write!(f, "Or"),
Port::Lshift(_) => write!(f, "Lshift"),
Port::Rshift(_) => write!(f, "Rshift"),
}
}
}
fn parse_input(input: &str) -> IResult<&str, Vec<Instruction>> {
let (input, result) = separated_list1(
multispace1,
alt((
tuple((complete::u16::<&str, _>, tag(" -> "), alpha1))
.map(|(val, _, target)| Instruction::Assign(val, target.to_string())),
tuple((alpha1::<&str, _>, tag(" -> "), alpha1)).map(|(source, _, target)| {
Instruction::Connect(source.to_string(), target.to_string())
}),
tuple((tag("NOT "), alpha1::<&str, _>, tag(" -> "), alpha1)).map(
|(_, source, _, target)| Instruction::Not(source.to_string(), target.to_string()),
),
tuple((alpha1::<&str, _>, tag(" AND "), alpha1, tag(" -> "), alpha1)).map(
|(source_1, _, source_2, _, target)| {
Instruction::And(
source_1.to_string(),
source_2.to_string(),
target.to_string(),
)
},
),
tuple((tag("1 AND "), alpha1::<&str, _>, tag(" -> "), alpha1)).map(
|(_, source, _, target)| {
Instruction::OneAnd(source.to_string(), target.to_string())
},
),
tuple((alpha1::<&str, _>, tag(" OR "), alpha1, tag(" -> "), alpha1)).map(
|(source_1, _, source_2, _, target)| {
Instruction::Or(
source_1.to_string(),
source_2.to_string(),
target.to_string(),
)
},
),
tuple((
alpha1::<&str, _>,
tag(" LSHIFT "),
complete::u16,
tag(" -> "),
alpha1,
))
.map(|(source, _, shift_amount, _, target)| {
Instruction::Lshift(shift_amount, source.to_string(), target.to_string())
}),
tuple((
alpha1::<&str, _>,
tag(" RSHIFT "),
complete::u16,
tag(" -> "),
alpha1,
))
.map(|(source, _, shift_amount, _, target)| {
Instruction::Rshift(shift_amount, source.to_string(), target.to_string())
}),
)),
)(input)?;
Ok((input, result))
}
type Node = (Port, Vec<String>, Option<u16>);
fn solve_first(instructions: &[Instruction], output_wire: &str) -> (u16, HashMap<String, Node>) {
// Because the graph is centered around the edges instead of nodes, we have to go through a lot of gymnastics to build it
let mut signals = HashMap::<String, Node>::new();
// For OneAnd operator
signals.insert("one".to_string(), (Port::Noop, vec![], Some(1)));
for instruction in instructions {
match instruction {
Instruction::Assign(value, wire) => {
signals.insert(wire.to_owned(), (Port::Noop, vec![], Some(*value)));
}
Instruction::Connect(source, target) => {
signals.insert(
target.to_owned(),
(Port::Noop, vec![source.to_owned()], None),
);
}
// We could already preprocess a bit by calculating the result if we know the input, but that might make it overly complicated a this point
// Instead, we do the actual traversal and calculations later
Instruction::Not(source, target) => {
signals.insert(
target.to_owned(),
(Port::Not, vec![source.to_owned()], None),
);
}
Instruction::And(source_1, source_2, target) => {
signals.insert(
target.to_owned(),
(
Port::And,
vec![source_1.to_owned(), source_2.to_owned()],
None,
),
);
}
Instruction::OneAnd(source, target) => {
signals.insert(
target.to_owned(),
(Port::And, vec!["one".to_string(), source.to_owned()], None),
);
}
Instruction::Or(source_1, source_2, target) => {
signals.insert(
target.to_owned(),
(
Port::Or,
vec![source_1.to_owned(), source_2.to_owned()],
None,
),
);
}
Instruction::Lshift(shift, source, target) => {
signals.insert(
target.to_owned(),
(Port::Lshift(*shift), vec![source.to_owned()], None),
);
}
Instruction::Rshift(shift, source, target) => {
signals.insert(
target.to_owned(),
(Port::Rshift(*shift), vec![source.to_owned()], None),
);
}
};
}
let mut edges_to_calc = vec![output_wire.to_string()];
while !edges_to_calc.is_empty() {
let edge_to_calc = edges_to_calc.pop().unwrap();
if let Some(signal) = signals.get(&edge_to_calc) {
match signal {
(port, incoming_wires, None) => {
let incoming_signals: Vec<Option<u16>> = incoming_wires
.iter()
.map(|x| match signals.get(x).unwrap() {
(_, _, None) => None,
(_, _, s) => s.to_owned(),
})
.collect();
if incoming_signals.iter().any(|s| s.is_none()) {
edges_to_calc.push(edge_to_calc);
for (s, w) in zip(incoming_signals, incoming_wires) {
if s.is_none() {
edges_to_calc.push(w.to_owned());
}
}
} else {
match port {
Port::Noop => {
assert_eq!(incoming_signals.len(), 1);
signals.insert(
edge_to_calc.to_owned(),
(*port, incoming_wires.to_owned(), incoming_signals[0]),
);
}
Port::Not => {
assert_eq!(incoming_signals.len(), 1);
signals.insert(
edge_to_calc.to_owned(),
(
*port,
incoming_wires.to_owned(),
Some(!incoming_signals[0].unwrap()),
),
);
}
Port::And => {
assert_eq!(incoming_signals.len(), 2);
signals.insert(
edge_to_calc.to_owned(),
(
*port,
incoming_wires.to_owned(),
Some(
incoming_signals[0].unwrap()
& incoming_signals[1].unwrap(),
),
),
);
}
Port::Or => {
assert_eq!(incoming_signals.len(), 2);
signals.insert(
edge_to_calc.to_owned(),
(
*port,
incoming_wires.to_owned(),
Some(
incoming_signals[0].unwrap()
| incoming_signals[1].unwrap(),
),
),
);
}
Port::Lshift(shift) => {
assert_eq!(incoming_signals.len(), 1);
signals.insert(
edge_to_calc.to_owned(),
(
*port,
incoming_wires.to_owned(),
Some(incoming_signals[0].unwrap() << shift),
),
);
}
Port::Rshift(shift) => {
assert_eq!(incoming_signals.len(), 1);
signals.insert(
edge_to_calc.to_owned(),
(
*port,
incoming_wires.to_owned(),
Some(incoming_signals[0].unwrap() >> shift),
),
);
}
}
}
}
(_, _, Some(_)) => { /* do nothing */ }
}
} else {
unreachable!();
}
}
let result = signals.get(&output_wire.to_string()).unwrap().to_owned();
match result {
(_, _, None) => (0, HashMap::new()),
(_, _, Some(s)) => (s, signals.to_owned()),
}
}
fn main() {
println!("Hello, this is Patrick!");
let input_txt = include_str!("../input.txt");
let (_, instructions) = parse_input(input_txt).unwrap();
let (first_result, _) = solve_first(&instructions[..], "a");
println!("The provided signal at wire 'a' is {}", first_result);
}

View File

@@ -0,0 +1,8 @@
123 -> x
456 -> y
x AND y -> d
x OR y -> e
x LSHIFT 2 -> f
y RSHIFT 2 -> g
NOT x -> h
NOT y -> i