Skip to content

Commit 4966679

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

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) | 36 |
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 = FastSet::new();
60+
let mut swapped = FastSet::new();
61+
62+
for &[left, kind, right, _, _] in gates {
63+
lookup.insert((left, kind));
64+
lookup.insert((right, kind));
65+
}
66+
67+
for &[left, kind, right, _, to] in gates {
68+
if kind == "AND" {
69+
// Check that all AND gates point to an OR, except for first AND.
70+
if left != "x00" && right != "x00" && !lookup.contains(&(to, "OR")) {
71+
swapped.insert(to);
72+
}
73+
}
74+
75+
if kind == "OR" {
76+
// Check that only XOR gates point to output, except for last carry which is OR.
77+
if to.starts_with('z') && to != "z45" {
78+
swapped.insert(to);
79+
}
80+
// OR can never point to OR.
81+
if lookup.contains(&(to, "OR")) {
82+
swapped.insert(to);
83+
}
84+
}
85+
86+
if kind == "XOR" {
87+
if left.starts_with('x') || right.starts_with('x') {
88+
// Check that first level XOR points to second level XOR, except for first XOR.
89+
if left != "x00" && right != "x00" && !lookup.contains(&(to, "XOR")) {
90+
swapped.insert(to);
91+
}
92+
} else {
93+
// Second level XOR must point to output.
94+
if !to.starts_with('z') {
95+
swapped.insert(to);
96+
}
97+
}
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)