Skip to content

Commit 3192ebe

Browse files
committed
Day 24 down
1 parent 9c63725 commit 3192ebe

File tree

6 files changed

+320
-3
lines changed

6 files changed

+320
-3
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
1010

1111
| Year | Completed |
1212
| :---: | :---: |
13-
| [2024](aoc2024) | 46/50 |
13+
| [2024](aoc2024) | 48/50 |
1414
| [2023](aoc2023) | 50/50 |
1515

1616
---

aoc2024/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
2929
| [Day 21](https://adventofcode.com/2024/day/21) | [code](src/bin/21.rs) |||
3030
| [Day 22](https://adventofcode.com/2024/day/22) | [code](src/bin/22.rs) |||
3131
| [Day 23](https://adventofcode.com/2024/day/23) | [code](src/bin/23.rs) |||
32-
| [Day 24](https://adventofcode.com/2024/day/24) | [code](src/bin/24.rs) | _ | _ |
32+
| [Day 24](https://adventofcode.com/2024/day/24) | [code](src/bin/24.rs) | | |
3333
| [Day 25](https://adventofcode.com/2024/day/25) | [code](src/bin/25.rs) | _ | _ |
3434

3535
---

aoc2024/data/examples/24-1.txt

+47
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

aoc2024/data/examples/24.txt

+10
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

aoc2024/src/bin/23.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ fn largest_containing<'a>(
4444
tried_containing: &mut HashSet<String>,
4545
cons: &'a HashMap<&'a str, Vec<&'a str>>,
4646
) -> String {
47-
let item = containing.iter().next().unwrap();
47+
let item = containing.first().unwrap();
4848
let v = cons.get(item).unwrap();
4949
let res = v
5050
.iter()

aoc2024/src/bin/24.rs

+260
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
use std::collections::{HashMap, HashSet, VecDeque};
2+
3+
use aoc_utils::*;
4+
use itertools::Itertools;
5+
6+
advent_of_code::solution!(24);
7+
8+
#[derive(Debug, Clone)]
9+
struct RuleParts {
10+
left: String,
11+
right: String,
12+
out: String,
13+
}
14+
15+
#[derive(Debug, Clone)]
16+
enum Rule {
17+
And(RuleParts),
18+
Or(RuleParts),
19+
Xor(RuleParts),
20+
}
21+
22+
impl Rule {
23+
fn from_strs(left: &str, rule: &str, right: &str, out: &str) -> Self {
24+
match rule {
25+
"AND" => Rule::And(RuleParts {
26+
left: left.to_owned(),
27+
right: right.to_owned(),
28+
out: out.to_owned(),
29+
}),
30+
"OR" => Rule::Or(RuleParts {
31+
left: left.to_owned(),
32+
right: right.to_owned(),
33+
out: out.to_owned(),
34+
}),
35+
"XOR" => Rule::Xor(RuleParts {
36+
left: left.to_owned(),
37+
right: right.to_owned(),
38+
out: out.to_owned(),
39+
}),
40+
_ => panic!("Unknown rule: {}", rule),
41+
}
42+
}
43+
44+
fn left(&self) -> &str {
45+
match self {
46+
Rule::And(parts) => &parts.left,
47+
Rule::Or(parts) => &parts.left,
48+
Rule::Xor(parts) => &parts.left,
49+
}
50+
}
51+
52+
fn right(&self) -> &str {
53+
match self {
54+
Rule::And(parts) => &parts.right,
55+
Rule::Or(parts) => &parts.right,
56+
Rule::Xor(parts) => &parts.right,
57+
}
58+
}
59+
60+
fn out(&self) -> &str {
61+
match self {
62+
Rule::And(parts) => &parts.out,
63+
Rule::Or(parts) => &parts.out,
64+
Rule::Xor(parts) => &parts.out,
65+
}
66+
}
67+
68+
fn apply(&self, registers: &mut HashMap<String, u64>) {
69+
let left = registers.get(self.left()).unwrap();
70+
let right = registers.get(self.right()).unwrap();
71+
let out = match self {
72+
Rule::And(_) => left & right,
73+
Rule::Or(_) => left | right,
74+
Rule::Xor(_) => left ^ right,
75+
};
76+
registers.insert(self.out().to_owned(), out);
77+
}
78+
}
79+
80+
fn parse_input(input: &str) -> (HashMap<String, u64>, Vec<Rule>) {
81+
let blocks = input.blocks();
82+
let registers = blocks[0]
83+
.mlines(|s| {
84+
let parts = s.split_once(": ").expect("Should have a colon");
85+
86+
(
87+
parts.0.to_owned(),
88+
parts.1.parse::<u64>().expect("Should be a number"),
89+
)
90+
})
91+
.into_iter()
92+
.collect::<HashMap<String, u64>>();
93+
let rules = blocks[1].mlines(|l| {
94+
let mut parts = l.split_whitespace();
95+
Rule::from_strs(
96+
parts.next().unwrap(),
97+
parts.next().unwrap(),
98+
parts.next().unwrap(),
99+
parts.nth(1).unwrap(),
100+
)
101+
});
102+
(registers, rules)
103+
}
104+
105+
pub fn part_one(input: &str) -> Option<u64> {
106+
let (mut registers, rules) = parse_input(input);
107+
let mut queue = VecDeque::from(rules);
108+
while !queue.is_empty() {
109+
let rule = queue.pop_front().unwrap();
110+
if !registers.contains_key(rule.left()) || !registers.contains_key(rule.right()) {
111+
queue.push_back(rule);
112+
continue;
113+
}
114+
rule.apply(&mut registers);
115+
}
116+
let mut zs = registers
117+
.into_iter()
118+
.filter(|(k, _)| k.starts_with("z"))
119+
.collect::<Vec<_>>();
120+
zs.sort();
121+
Some(zs.into_iter().rev().fold(0, |acc, (_, v)| acc << 1 | v))
122+
}
123+
124+
pub fn part_two(input: &str) -> Option<String> {
125+
let (registers, rules) = parse_input(input);
126+
let max_number = registers.len() / 2;
127+
128+
// FULL ADDER
129+
// (first bits aren't a full adder)
130+
// (for last FA, COUT is the extra output)
131+
//
132+
// Xn XOR Yn -> VAL0 <= FAGate0
133+
// Xn AND Yn -> VAL1 <= FAGate1
134+
// VAL0 AND CIN -> VAL2 <= FAGate2
135+
// VAL0 XOR CIN -> Zn <= FAGate3
136+
// VAL1 OR VAL2 -> COUT <= FAGate4
137+
138+
let mut bad_outputs = HashSet::new();
139+
// check FAGate0 gates for Zns
140+
// each of these should be An XOR Bn -> VAL0n
141+
// except for the first one, which should be x00 XOR y00 -> z00
142+
let fa0s = rules
143+
.iter()
144+
.filter(|r| {
145+
if let Rule::Xor(parts) = r {
146+
(parts.left.starts_with("x") || parts.right.starts_with("x"))
147+
&& (parts.left.starts_with("y") || parts.right.starts_with("y"))
148+
} else {
149+
false
150+
}
151+
})
152+
.collect_vec();
153+
fa0s.iter().for_each(|r| {
154+
if r.out().starts_with("z") && r.out() != "z00" && (r.left() != "x00" || r.right() != "x00")
155+
{
156+
bad_outputs.insert(r.out());
157+
}
158+
});
159+
160+
// call rules that do not take Xn & Yn gates as inputs indirect
161+
// check all XOR gates that are indirect (FAGate3)
162+
// each of these should be outputting to a zXX
163+
let fa3s = rules
164+
.iter()
165+
.filter(|r| {
166+
if let Rule::Xor(parts) = r {
167+
!(parts.left.starts_with("x")
168+
|| parts.right.starts_with("x")
169+
|| parts.left.starts_with("y")
170+
|| parts.right.starts_with("y"))
171+
} else {
172+
false
173+
}
174+
})
175+
.collect_vec();
176+
fa3s.iter().for_each(|r| {
177+
if !r.out().starts_with("z") {
178+
bad_outputs.insert(r.out());
179+
}
180+
});
181+
182+
// check all output gates
183+
// each of these should be VAL0 XOR CIN -> Zn (FAGate3)
184+
// except for the last one, which should be VAL1 OR VAL2 -> COUT
185+
let outputs = rules
186+
.iter()
187+
.filter(|r| r.out().starts_with("z"))
188+
.collect_vec();
189+
let max_output = format!("z{:02}", max_number);
190+
outputs.iter().for_each(|r| {
191+
if r.out() == max_output {
192+
if !matches!(r, Rule::Or(_)) {
193+
bad_outputs.insert(r.out());
194+
}
195+
} else {
196+
if !matches!(r, Rule::Xor(_)) {
197+
bad_outputs.insert(r.out());
198+
}
199+
}
200+
});
201+
202+
// all FAGate0 gates MUST be inputs to a FAGate3 gate
203+
let bad_fa0s = fa0s
204+
.iter()
205+
.filter(|r| {
206+
if r.out() == "z00" {
207+
return false;
208+
}
209+
if bad_outputs.contains(r.out()) {
210+
// skip - already a bad output
211+
return false;
212+
}
213+
!fa3s
214+
.iter()
215+
.any(|r2| r2.left() == r.out() || r2.right() == r.out())
216+
})
217+
.collect_vec();
218+
bad_fa0s.iter().for_each(|r| {
219+
bad_outputs.insert(r.out());
220+
let intended_out = format!("z{}", &r.left()[1..]);
221+
let expected_fa3 = fa3s
222+
.iter()
223+
.find(|r2| r2.out() == intended_out)
224+
.expect("Shoud have a next");
225+
let left = expected_fa3.left();
226+
let right = expected_fa3.right();
227+
// find an FAGate4 that outputs a c_in used as input to the expected FAGate3
228+
// the other input should be the output of the bad FAGate0
229+
rules.iter().for_each(|r3| {
230+
if matches!(r3, Rule::Or(_)) {
231+
if r3.out() == left {
232+
bad_outputs.insert(right);
233+
}
234+
if r3.out() == right {
235+
bad_outputs.insert(left);
236+
}
237+
}
238+
});
239+
});
240+
Some(bad_outputs.iter().sorted().join(","))
241+
}
242+
243+
#[cfg(test)]
244+
mod tests {
245+
use super::*;
246+
247+
#[test]
248+
fn test_part_one() {
249+
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
250+
assert_eq!(result, Some(4));
251+
}
252+
253+
#[test]
254+
fn test_part_one_2() {
255+
let result = part_one(&advent_of_code::template::read_file_part(
256+
"examples", DAY, 1,
257+
));
258+
assert_eq!(result, Some(2024));
259+
}
260+
}

0 commit comments

Comments
 (0)