Skip to content

Commit 8a0c070

Browse files
committed
Rules based on structure to detect swapped gate outputs
1 parent ccef6dd commit 8a0c070

File tree

3 files changed

+69
-118
lines changed

3 files changed

+69
-118
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
9595
| 21 | [Keypad Conundrum](https://adventofcode.com/2024/day/21) | [Source](src/year2024/day21.rs) | 111 |
9696
| 22 | [Monkey Market](https://adventofcode.com/2024/day/22) | [Source](src/year2024/day22.rs) | 1350 |
9797
| 23 | [LAN Party](https://adventofcode.com/2024/day/23) | [Source](src/year2024/day23.rs) | 43 |
98-
| 24 | [Crossed Wires](https://adventofcode.com/2024/day/24) | [Source](src/year2024/day24.rs) | - |
98+
| 24 | [Crossed Wires](https://adventofcode.com/2024/day/24) | [Source](src/year2024/day24.rs) | 38 |
9999

100100
## 2023
101101

src/year2024/day24.rs

Lines changed: 66 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,44 @@
11
//! # Crossed Wires
22
use crate::util::hash::*;
3+
use crate::util::iter::*;
34
use crate::util::parse::*;
45

5-
pub fn parse(input: &str) -> &str {
6-
input
7-
}
6+
type Input<'a> = (&'a str, Vec<[&'a str; 5]>);
87

9-
pub fn part1(input: &str) -> u64 {
8+
pub fn parse(input: &str) -> Input<'_> {
109
let (prefix, suffix) = input.split_once("\n\n").unwrap();
10+
let gates = suffix.split_ascii_whitespace().chunk::<5>().collect();
11+
(prefix, gates)
12+
}
13+
14+
pub fn part1(input: &Input<'_>) -> u64 {
15+
let (prefix, gates) = input;
1116

1217
let mut names = FastMap::new();
1318
let mut cache = FastMap::new();
1419
let mut ops = FastMap::new();
1520

1621
for line in prefix.lines() {
17-
let (key, value) = line.split_once(": ").unwrap();
22+
let prefix = &line[..3];
23+
let suffix = &line[5..];
1824

1925
let size = names.len();
20-
let index = *names.entry(key).or_insert(size);
26+
let index = *names.entry(prefix).or_insert(size);
2127

22-
cache.insert(index, value.unsigned::<u64>());
28+
cache.insert(index, suffix.unsigned());
2329
}
2430

25-
for line in suffix.lines() {
26-
let tokens: Vec<_> = line.split(' ').collect();
27-
let op = tokens[1];
28-
31+
for &[left, kind, right, _, to] in gates {
2932
let size = names.len();
30-
let left = *names.entry(tokens[0]).or_insert(size);
33+
let left = *names.entry(left).or_insert(size);
3134

3235
let size = names.len();
33-
let right = *names.entry(tokens[2]).or_insert(size);
36+
let right = *names.entry(right).or_insert(size);
3437

3538
let size = names.len();
36-
let to = *names.entry(tokens[4]).or_insert(size);
39+
let to = *names.entry(to).or_insert(size);
3740

38-
ops.insert(to, (left, op, right));
41+
ops.insert(to, (left, kind, right));
3942
}
4043

4144
let mut result = 0;
@@ -50,105 +53,54 @@ pub fn part1(input: &str) -> u64 {
5053
result
5154
}
5255

53-
pub fn part2(_input: &str) -> String {
54-
// let (_, suffix) = input.split_once("\n\n").unwrap();
55-
// let mut wires = FastMap::new();
56-
57-
// println!("digraph G {{");
58-
59-
// for i in 0..46 {
60-
// if i < 45 {
61-
// let key = format!("x{i:02}");
62-
// println!(" {} [pos=\"{},{}!\"]", key, i * 2, 5);
63-
// let value = key.clone();
64-
// wires.insert(key, vec![value]);
65-
66-
// let key = format!("y{i:02}");
67-
// println!(" {} [pos=\"{},{}!\"]", key, i * 2 + 1, 5);
68-
// let value = key.clone();
69-
// wires.insert(key, vec![value]);
70-
// }
71-
72-
// let key = format!("z{i:02}");
73-
// println!(" {} [pos=\"{},{}!\"]", key, i * 2, 0);
74-
// }
75-
76-
// println!();
77-
78-
// for (name, line) in suffix.lines().enumerate() {
79-
// let tokens: Vec<_> = line.split(' ').collect();
80-
// let [_, _, _, _, to] = tokens[..] else { unreachable!() };
81-
// wires.entry(String::from(to)).or_insert_with(Vec::new).push(format!("{name}"));
82-
// }
83-
84-
// let mut second = FastMap::new();
85-
86-
// for (name, line) in suffix.lines().enumerate() {
87-
// let tokens: Vec<_> = line.split(' ').collect();
88-
// let [left, op, right, _, to] = tokens[..] else { unreachable!() };
89-
90-
// let shape = match op {
91-
// "AND" => "square",
92-
// "OR" => "hexagon",
93-
// "XOR" => "triangle",
94-
// _ => unreachable!(),
95-
// };
96-
97-
// if left.starts_with('x') || right.starts_with('x') {
98-
// let i: usize = left.unsigned();
99-
// if op == "AND" {
100-
// println!("{} [pos=\"{},{}!\"]", name, i * 2 + 1, 4);
101-
// second.insert(to, i);
102-
// }
103-
// if op == "XOR" {
104-
// println!("{} [pos=\"{},{}!\"]", name, i * 2, 4);
105-
// second.insert(to, i);
106-
// }
107-
// }
108-
// if to.starts_with('z') {
109-
// let i: usize = to.unsigned();
110-
// println!("{} [pos=\"{},{}!\"]", name, i * 2, 1);
111-
// }
112-
113-
// println!(" {name} [shape={shape}]");
114-
// for edge in &wires[&String::from(left)] {
115-
// println!(" {edge} -> {name} [label=\"{left}\"]");
116-
// }
117-
// for edge in &wires[&String::from(right)] {
118-
// println!(" {edge} -> {name} [label=\"{right}\"]");
119-
// }
120-
// }
121-
122-
// for (name, line) in suffix.lines().enumerate() {
123-
// let tokens: Vec<_> = line.split(' ').collect();
124-
// let [left, op, right, _, _] = tokens[..] else { unreachable!() };
125-
126-
// if op == "AND" {
127-
// if let Some(i) = second.get(left) {
128-
// println!("{} [pos=\"{},{}!\"]", name, i * 2 + 1, 3);
129-
// }
130-
// if let Some(i) = second.get(right) {
131-
// println!("{} [pos=\"{},{}!\"]", name, i * 2 + 1, 3);
132-
// }
133-
// }
134-
// if op == "OR" {
135-
// if let Some(i) = second.get(left) {
136-
// println!("{} [pos=\"{},{}!\"]", name, i * 2 + 1, 2);
137-
// }
138-
// if let Some(i) = second.get(right) {
139-
// println!("{} [pos=\"{},{}!\"]", name, i * 2 + 1, 2);
140-
// }
141-
// }
142-
// }
143-
144-
// for i in 0..46 {
145-
// let key = format!("z{i:02}");
146-
// for edge in &wires[&key] {
147-
// println!(" {edge} -> {key}");
148-
// }
149-
// }
150-
151-
String::from("n/a")
56+
pub fn part2(input: &Input<'_>) -> String {
57+
let (_, gates) = input;
58+
59+
let mut lookup = FastMap::new();
60+
let mut swapped = FastSet::new();
61+
62+
for &[left, kind, right, _, to] in gates {
63+
lookup.insert((left, kind), to);
64+
lookup.insert((right, kind), to);
65+
66+
// Check that only XOR gates points to output, except for last carry which is OR.
67+
if to.starts_with('z') {
68+
match kind {
69+
"XOR" => (),
70+
"OR" if to == "z45" => (),
71+
_ => {
72+
swapped.insert(to);
73+
}
74+
}
75+
}
76+
}
77+
78+
for i in 1..45 {
79+
let x = format!("x{i:02}");
80+
let z = format!("z{i:02}");
81+
82+
// Check that first level XOR points to second XOR that points to correct output.
83+
let to = lookup[&(x.as_str(), "XOR")];
84+
85+
if let Some(&next) = lookup.get(&(to, "XOR")) {
86+
if z != next {
87+
swapped.insert(next);
88+
}
89+
} else {
90+
swapped.insert(to);
91+
}
92+
93+
// Check that first level AND points to an OR.
94+
let to = lookup[&(x.as_str(), "AND")];
95+
96+
if !lookup.contains_key(&(to, "OR")) {
97+
swapped.insert(to);
98+
}
99+
}
100+
101+
let mut result: Vec<_> = swapped.into_iter().collect();
102+
result.sort_unstable();
103+
result.join(",")
152104
}
153105

154106
fn helper(

tests/year2024/day24.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,10 @@ tnw OR pbm -> gnj";
5252
#[test]
5353
fn part1_test() {
5454
let input = parse(EXAMPLE);
55-
assert_eq!(part1(input), 2024);
55+
assert_eq!(part1(&input), 2024);
5656
}
5757

5858
#[test]
5959
fn part2_test() {
60-
let input = parse(EXAMPLE);
61-
assert_eq!(part2(input), "n/a");
60+
// No test
6261
}

0 commit comments

Comments
 (0)