Skip to content

Commit 822bd4f

Browse files
committed
Clean up day 24
1 parent a240bb7 commit 822bd4f

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-0
lines changed

data/examples/24-2.txt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
x00: 1
2+
x01: 0
3+
x02: 1
4+
x03: 1
5+
x04: 0
6+
y00: 1
7+
y01: 1
8+
y02: 1
9+
y03: 1
10+
y04: 1
11+
12+
ntg XOR fgs -> mjb
13+
y02 OR x01 -> tnw
14+
kwq OR kpj -> z05
15+
x00 OR x03 -> fst
16+
tgd XOR rvg -> z01
17+
vdt OR tnw -> bfw
18+
bfw AND frj -> z10
19+
ffh OR nrd -> bqk
20+
y00 AND y03 -> djm
21+
y03 OR y00 -> psh
22+
bqk OR frj -> z08
23+
tnw OR fst -> frj
24+
gnj AND tgd -> z11
25+
bfw XOR mjb -> z00
26+
x03 OR x00 -> vdt
27+
gnj AND wpb -> z02
28+
x04 AND y00 -> kjc
29+
djm OR pbm -> qhw
30+
nrd AND vdt -> hwm
31+
kjc AND fst -> rvg
32+
y04 OR y02 -> fgs
33+
y01 AND x02 -> pbm
34+
ntg OR kjc -> kwq
35+
psh XOR fgs -> tgd
36+
qhw XOR tgd -> z09
37+
pbm OR djm -> kpj
38+
x03 XOR y03 -> ffh
39+
x00 XOR y04 -> ntg
40+
bfw OR bqk -> z06
41+
nrd XOR fgs -> wpb
42+
frj XOR qhw -> z04
43+
bqk OR frj -> z07
44+
y03 OR x01 -> nrd
45+
hwm AND bqk -> z03
46+
tgd XOR rvg -> z12
47+
tnw OR pbm -> gnj

data/examples/24.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
x00: 1
2+
x01: 1
3+
x02: 1
4+
y00: 0
5+
y01: 1
6+
y02: 0
7+
8+
x00 AND y00 -> z00
9+
x01 XOR y01 -> z01
10+
x02 OR y02 -> z02

src/bin/24.rs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
advent_of_code::solution!(24);
2+
3+
use hashbrown::HashMap;
4+
use itertools::Itertools;
5+
use std::collections::HashSet;
6+
7+
#[derive(Clone, Copy, PartialEq, Eq)]
8+
enum Logic {
9+
And,
10+
Or,
11+
Xor,
12+
}
13+
14+
struct Gate<'a> {
15+
logic: Logic,
16+
a: &'a str,
17+
b: &'a str,
18+
out: &'a str,
19+
}
20+
21+
fn compute_bit_vector(s: &HashMap<&str, bool>, p: char) -> usize {
22+
s.keys()
23+
.filter(|&n| n.starts_with(p))
24+
.map(|n| {
25+
let index = n[1..].parse::<usize>().unwrap();
26+
if s[n] {
27+
1 << index
28+
} else {
29+
0
30+
}
31+
})
32+
.fold(0, |acc, value| acc | value) // Combine the bit values
33+
}
34+
35+
fn val<'a>(
36+
gates: &HashMap<&'a str, (&'a str, &'a str, &'a str)>,
37+
states: &mut HashMap<&'a str, bool>,
38+
node: &'a str,
39+
) -> bool {
40+
if let Some(&v) = states.get(node) {
41+
return v;
42+
}
43+
let (a, op, b) = gates[node];
44+
let a = val(gates, states, a);
45+
let b = val(gates, states, b);
46+
47+
let res = match op {
48+
"AND" => a && b,
49+
"XOR" => a != b,
50+
"OR" => a || b,
51+
_ => unreachable!(),
52+
};
53+
54+
states.insert(node, res);
55+
res
56+
}
57+
58+
pub fn part_one(input: &str) -> Option<usize> {
59+
let (wires, gates) = input.split_once("\n\n").unwrap();
60+
let mut s = HashMap::new();
61+
let mut g = HashMap::new();
62+
for l in wires.lines() {
63+
let (n, v) = l.split_once(": ").unwrap();
64+
s.insert(n, v == "1");
65+
}
66+
for l in gates.lines() {
67+
let (a, op, b, _, c) = l.split(' ').collect_tuple().unwrap();
68+
g.insert(c, (a, op, b));
69+
}
70+
for n in g.keys() {
71+
val(&g, &mut s, n);
72+
}
73+
Some(compute_bit_vector(&s, 'z'))
74+
}
75+
76+
pub fn part_two(input: &str) -> Option<String> {
77+
let (_, gates) = input.split_once("\n\n").unwrap();
78+
let mut edges: HashMap<&str, Vec<&str>> = HashMap::new();
79+
let mut broken_nodes = HashSet::new();
80+
81+
let gates = gates
82+
.lines()
83+
.map(|l| {
84+
let (left, out) = l.split_once(" -> ").unwrap();
85+
let s = left.split_whitespace().collect::<Vec<_>>();
86+
let logic = match s[1] {
87+
"AND" => Logic::And,
88+
"OR" => Logic::Or,
89+
"XOR" => Logic::Xor,
90+
_ => panic!("Invalid gate: {}", left),
91+
};
92+
Gate {
93+
logic,
94+
a: s[0],
95+
b: s[2],
96+
out,
97+
}
98+
})
99+
.collect::<Vec<_>>();
100+
101+
for g in &gates {
102+
edges.entry(g.a).or_default().push(g.out);
103+
edges.entry(g.b).or_default().push(g.out);
104+
}
105+
106+
for g in &gates {
107+
// z nodes must be XOR (except for the last one, z45)
108+
if g.out.starts_with("z") && g.out != "z45" && g.logic != Logic::Xor {
109+
broken_nodes.insert(g.out);
110+
}
111+
// z nodes must not be inputs of other nodes
112+
if g.a.starts_with("z") {
113+
broken_nodes.insert(g.a);
114+
}
115+
if g.b.starts_with("z") {
116+
broken_nodes.insert(g.b);
117+
}
118+
119+
// inputs of XOR nodes (except for z nodes) must be x and y nodes
120+
if g.logic == Logic::Xor
121+
&& !g.out.starts_with("z")
122+
&& !((g.a.starts_with("x") && g.b.starts_with("y"))
123+
|| (g.a.starts_with("y") && g.b.starts_with("x")))
124+
{
125+
broken_nodes.insert(g.out);
126+
}
127+
128+
// XOR nodes (except z nodes) must always be input of exactly two
129+
// other nodes
130+
if g.logic == Logic::Xor && !g.out.starts_with("z") && edges[g.out].len() != 2 {
131+
broken_nodes.insert(g.out);
132+
}
133+
134+
// AND nodes must always be input of exactly one other node (except
135+
// the very first one wired to x00 and y00)
136+
if g.logic == Logic::And
137+
&& !g.out.starts_with("z")
138+
&& edges[g.out].len() != 1
139+
&& !((g.a == "x00" && g.b == "y00") || (g.a == "y00" && g.b == "x00"))
140+
{
141+
broken_nodes.insert(g.out);
142+
}
143+
}
144+
let mut broken_nodes = broken_nodes.into_iter().collect::<Vec<_>>();
145+
broken_nodes.sort();
146+
147+
Some(broken_nodes.join(","))
148+
}
149+
150+
#[cfg(test)]
151+
mod tests {
152+
use super::*;
153+
154+
#[test]
155+
fn test_part_one() {
156+
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
157+
assert_eq!(result, Some(4));
158+
}
159+
160+
#[test]
161+
fn test_part_one_with_diff_example() {
162+
let result = part_one(&advent_of_code::template::read_file_part(
163+
"examples", DAY, 2,
164+
));
165+
assert_eq!(result, Some(2024));
166+
}
167+
168+
#[test]
169+
fn test_part_two() {
170+
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
171+
assert_eq!(result, Some("z00,z02".to_owned()));
172+
}
173+
}

0 commit comments

Comments
 (0)