Skip to content

Commit 825c7be

Browse files
committed
day24 πŸ”Œ
1 parent 11a3814 commit 825c7be

File tree

8 files changed

+339
-7
lines changed

8 files changed

+339
-7
lines changed

β€Ž2024/README.mdβ€Ž

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Advent of Code in Rust πŸ¦€
22

33
![AoC2024](https://img.shields.io/badge/Advent_of_Code-2024-8A2BE2)
4-
![Stars: 46](https://img.shields.io/badge/Stars-46⭐-blue)
5-
![Rust: 24](https://img.shields.io/badge/Rust-24-cyan?logo=Rust)
4+
![Stars: 48](https://img.shields.io/badge/Stars-48⭐-blue)
5+
![Rust: 25](https://img.shields.io/badge/Rust-25-cyan?logo=Rust)
66
![Python: 9](https://img.shields.io/badge/Python-9-cyan?logo=Python)
77

8-
## 2024 ([Calendar](https://adventofcode.com/2024)) ([Solutions](../2024/)) : 46⭐
8+
## 2024 ([Calendar](https://adventofcode.com/2024)) ([Solutions](../2024/)) : 48⭐
99

1010
Puzzle | Stars | Languages
1111
---------------------------------------------------------------------- | ----- | -----------
@@ -32,3 +32,4 @@ Puzzle | Stars |
3232
[Day 21: Keypad Conundrum](https://adventofcode.com/2024/day/21) | ⭐⭐ | [![Rust](../scripts/assets/rust.png)](../2024/day21/day21.rs)
3333
[Day 22: Monkey Market](https://adventofcode.com/2024/day/22) | ⭐⭐ | [![Rust](../scripts/assets/rust.png)](../2024/day22/day22.rs)
3434
[Day 23: LAN Party](https://adventofcode.com/2024/day/23) | ⭐⭐ | [![Rust](../scripts/assets/rust.png)](../2024/day23/day23.rs) [![Python](../scripts/assets/python.png)](../2024/day23/day23.py)
35+
[Day 24: Crossed Wires](https://adventofcode.com/2024/day/24) | ⭐⭐ | [![Rust](../scripts/assets/rust.png)](../2024/day24/day24.rs)

β€Ž2024/day24/Cargo.tomlβ€Ž

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "day24"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
aoc = { path = "../../aoc" }
8+
9+
[[bin]]
10+
name = "day24"
11+
path = "day24.rs"

β€Ž2024/day24/day24.rsβ€Ž

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
//! [Day 24: Crossed Wires](https://adventofcode.com/2024/day/24)
2+
3+
// Circuit diagrams are copied from:
4+
// https://www.researchgate.net/publication/349727409_PyQUBO_Python_Library_for_Mapping_Combinatorial_Optimization_Problems_to_QUBO_Form
5+
//
6+
// @unknown{unknown,
7+
// author = {Zaman, Mashiyat and Tanahashi, Kotaro and Tanaka, Shu},
8+
// year = {2021},
9+
// month = {03},
10+
// pages = {},
11+
// title = {PyQUBO: Python Library for Mapping Combinatorial Optimization Problems to QUBO Form},
12+
// doi = {10.48550/arXiv.2103.01708}
13+
// }
14+
//
15+
16+
use std::collections::{HashMap, HashSet};
17+
18+
#[derive(Eq, PartialEq, Hash, Clone, Debug)]
19+
enum Role {
20+
CarryOut, // the Cout wire
21+
IntXorXor, // intermediate wire between the two XOR gates
22+
ABAndGate, // intermediate wires between AB and the (bottom) AND gate
23+
AndGateWires, // wiring of the AND gates
24+
SumOut, // the S wire
25+
}
26+
27+
#[derive(Eq, PartialEq, Hash, Clone)]
28+
enum Operation {
29+
And,
30+
Or,
31+
Xor,
32+
}
33+
34+
impl Operation {
35+
fn from(s: &str) -> Self {
36+
match s {
37+
"AND" => Self::And,
38+
"OR" => Self::Or,
39+
"XOR" => Self::Xor,
40+
_ => panic!("unknown op {s}"),
41+
}
42+
}
43+
44+
const fn eval(&self, a: u8, b: u8) -> u8 {
45+
match self {
46+
Self::And => a & b,
47+
Self::Or => a | b,
48+
Self::Xor => a ^ b,
49+
}
50+
}
51+
}
52+
53+
#[derive(Eq, PartialEq, Hash, Clone)]
54+
struct Gate {
55+
a: String, // input wire
56+
b: String, // input wire
57+
op: Operation, // type of gate
58+
r: String, // output wire
59+
}
60+
61+
fn is_role(set: &HashSet<Role>, f: &Role) -> bool {
62+
set.len() == 1 && (set.iter().next().unwrap() == f)
63+
}
64+
65+
fn is_roles(set: &HashSet<Role>, f1: &Role, f2: &Role) -> bool {
66+
if set.len() != 2 {
67+
return false;
68+
}
69+
70+
let roles: Vec<_> = set.iter().collect();
71+
(roles[0] == f1 && roles[1] == f2) || (roles[0] == f2 && roles[1] == f1)
72+
}
73+
74+
struct Puzzle {
75+
// data: String,
76+
wires: HashMap<String, u8>,
77+
gates: Vec<Gate>,
78+
}
79+
80+
impl Puzzle {
81+
fn new() -> Self {
82+
Self {
83+
wires: HashMap::new(),
84+
gates: Vec::new(),
85+
}
86+
}
87+
88+
/// Get the puzzle input.
89+
fn configure(&mut self, path: &str) {
90+
let data = std::fs::read_to_string(path).unwrap_or_else(|_| {
91+
eprintln!("cannot read input file {path}");
92+
std::process::exit(1);
93+
});
94+
95+
for line in data.lines() {
96+
if line.contains(": ") {
97+
let (wire, value) = line.split_once(": ").unwrap();
98+
self.wires.insert(wire.to_string(), value.parse().unwrap());
99+
}
100+
if line.contains(" -> ") {
101+
let v = line.split_ascii_whitespace().collect::<Vec<_>>();
102+
103+
let gate = Gate {
104+
a: v[0].to_string(),
105+
op: Operation::from(v[1]),
106+
b: v[2].to_string(),
107+
r: v[4].to_string(),
108+
};
109+
110+
self.gates.push(gate);
111+
}
112+
}
113+
}
114+
115+
/// Solve part one.
116+
fn part1(&self) -> u64 {
117+
let mut waiting_gates = self.gates.iter().collect::<Vec<_>>();
118+
let mut wires = self.wires.clone();
119+
120+
while !waiting_gates.is_empty() {
121+
let mut next_waiting = Vec::new();
122+
123+
for gate in &waiting_gates {
124+
if let Some(&a) = wires.get(&gate.a) {
125+
if let Some(&b) = wires.get(&gate.b) {
126+
let r = gate.op.eval(a, b);
127+
*wires.entry(gate.r.to_string()).or_default() = r;
128+
continue;
129+
}
130+
}
131+
132+
next_waiting.push(*gate);
133+
}
134+
135+
waiting_gates = next_waiting;
136+
}
137+
138+
wires
139+
.iter()
140+
.filter(|(r, &v)| r.starts_with('z') && v == 1)
141+
.fold(0_u64, |acc, (r, _)| {
142+
acc | (1 << r[1..].parse::<u64>().unwrap())
143+
})
144+
}
145+
146+
/// Solve part two.
147+
fn part2(&self) -> String {
148+
let mut input_types: HashMap<&str, HashSet<Role>> = HashMap::new();
149+
let mut result_types: HashMap<&str, HashSet<Role>> = HashMap::new();
150+
151+
// analyse the role of each gate
152+
153+
for gate in &self.gates {
154+
if gate.a == "x00" && gate.b == "y00" || gate.b == "x00" && gate.a == "y00" {
155+
// ignore first half adder
156+
continue;
157+
}
158+
159+
let mut add_result_role =
160+
|r: &Role| result_types.entry(&gate.r).or_default().insert(r.clone());
161+
162+
// full adder
163+
if (gate.a.starts_with('x') && gate.b.starts_with('y'))
164+
|| (gate.a.starts_with('y') && gate.b.starts_with('x'))
165+
{
166+
add_result_role(match gate.op {
167+
Operation::Xor => &Role::IntXorXor, // xy connected to the 1st XOR gate: output is the wire between the both XOR
168+
Operation::And => &Role::ABAndGate, // xy wired to a AND gate
169+
Operation::Or => panic!("OR gate should be wired to x/y"),
170+
});
171+
} else {
172+
let role = match gate.op {
173+
Operation::Xor => &Role::SumOut, // actually the 2nd XOR gate
174+
Operation::And => &Role::AndGateWires, // connections of AND gates
175+
Operation::Or => &Role::CarryOut, // the only one OR gate is wired to Cout
176+
};
177+
178+
input_types.entry(&gate.a).or_default().insert(role.clone());
179+
input_types.entry(&gate.b).or_default().insert(role.clone());
180+
add_result_role(role);
181+
}
182+
}
183+
184+
// branch all logical adders
185+
186+
let last_z_wire = result_types
187+
.keys()
188+
.filter(|wire| wire.starts_with('z'))
189+
.max()
190+
.unwrap();
191+
192+
let mut bad_wires: Vec<&str> = Vec::new();
193+
194+
for wire in result_types.keys() {
195+
let inp = &input_types.entry(wire).or_default();
196+
let res = &result_types[wire];
197+
198+
if wire == last_z_wire && is_role(res, &Role::CarryOut) {
199+
// ok, last wire/bit of the result register should be wired to CarryOut
200+
continue;
201+
}
202+
203+
if inp.is_empty() && wire.starts_with('z') && is_role(res, &Role::SumOut) {
204+
// ok, other z wires are Sum outputs
205+
continue;
206+
}
207+
208+
if is_role(inp, &Role::CarryOut)
209+
&& (is_role(res, &Role::AndGateWires) || is_role(res, &Role::ABAndGate))
210+
{
211+
// ok: CarryOut should be wired to 2nd XOR or a AND gate
212+
continue;
213+
}
214+
215+
if is_roles(inp, &Role::SumOut, &Role::AndGateWires)
216+
&& (is_role(res, &Role::CarryOut) || is_role(res, &Role::IntXorXor))
217+
{
218+
// ok: Cin and Sum should be wired to Cout or
219+
continue;
220+
}
221+
222+
#[cfg(debug_assertions)]
223+
eprintln!("❌ {wire} : {inp:?} β†’ {res:?}");
224+
225+
// ⚠️ swapped wire pairs are not determined,
226+
// ⚠️ just the eight incorrectly wired
227+
228+
bad_wires.push(wire);
229+
}
230+
231+
bad_wires.sort_unstable();
232+
bad_wires.join(",")
233+
}
234+
}
235+
236+
fn main() {
237+
let args = aoc::parse_args();
238+
let mut puzzle = Puzzle::new();
239+
puzzle.configure(args.path.as_str());
240+
println!("{}", puzzle.part1());
241+
println!("{}", puzzle.part2());
242+
}
243+
244+
/// Test from puzzle input
245+
#[cfg(test)]
246+
mod test {
247+
use super::*;
248+
249+
#[test]
250+
fn test_part1() {
251+
let mut puzzle = Puzzle::new();
252+
puzzle.configure("sample_1.txt");
253+
assert_eq!(puzzle.part1(), 4);
254+
}
255+
256+
#[test]
257+
fn test_part2() {
258+
let mut puzzle = Puzzle::new();
259+
puzzle.configure("sample_2.txt");
260+
assert_eq!(puzzle.part1(), 2024);
261+
}
262+
}

β€Ž2024/day24/full_adder.pngβ€Ž

61.5 KB
Loading

β€Ž2024/day24/half_adder.pngβ€Ž

89 KB
Loading

β€Ž2024/day24/sample_1.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

β€Ž2024/day24/sample_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

β€ŽREADME.mdβ€Ž

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# [Advent of Code](https://adventofcode.com) in Rust πŸ¦€
22

3-
![Stars: 496](https://img.shields.io/badge/Stars-496⭐-blue)
4-
![Rust: 201](https://img.shields.io/badge/Rust-201-cyan?logo=Rust)
3+
![Stars: 498](https://img.shields.io/badge/Stars-498⭐-blue)
4+
![Rust: 202](https://img.shields.io/badge/Rust-202-cyan?logo=Rust)
55
![Python: 122](https://img.shields.io/badge/Python-122-cyan?logo=Python)
66

77
Solutions of [Advent of Code](https://adventofcode.com/) in [Rust](https://www.rust-lang.org), and sometimes in [Python](https://www.python.org/) and other languages.
88

99
Made for fun 😎 and to practice Rust. Many thanks to [Eric Wastl](https://twitter.com/ericwastl).
1010

11-
## 2024 (current event) ([Calendar](https://adventofcode.com/2024)) ([Solutions](2024/)) : 46⭐
11+
## 2024 (current event) ([Calendar](https://adventofcode.com/2024)) ([Solutions](2024/)) : 48⭐
1212

1313
Puzzle | Stars | Languages
1414
---------------------------------------------------------------------- | ----- | -----------
@@ -35,12 +35,13 @@ Puzzle | Stars |
3535
[Day 21: Keypad Conundrum](https://adventofcode.com/2024/day/21) | ⭐⭐ | [![Rust](./scripts/assets/rust.png)](./2024/day21/day21.rs)
3636
[Day 22: Monkey Market](https://adventofcode.com/2024/day/22) | ⭐⭐ | [![Rust](./scripts/assets/rust.png)](./2024/day22/day22.rs)
3737
[Day 23: LAN Party](https://adventofcode.com/2024/day/23) | ⭐⭐ | [![Rust](./scripts/assets/rust.png)](./2024/day23/day23.rs) [![Python](./scripts/assets/python.png)](./2024/day23/day23.py)
38+
[Day 24: Crossed Wires](https://adventofcode.com/2024/day/24) | ⭐⭐ | [![Rust](./scripts/assets/rust.png)](./2024/day24/day24.rs)
3839

3940
## All years
4041

4142
Calendar | Solutions | Stars | Rust | Python | πŸŽ„
4243
-------- | --------- | ----- | ---- | ------ | --
43-
[Advent of Code 2024](https://adventofcode.com/2024) | [Solutions](2024/README.md) | 46⭐ | 23 | 9 | 3
44+
[Advent of Code 2024](https://adventofcode.com/2024) | [Solutions](2024/README.md) | 48⭐ | 24 | 9 | 3
4445
[Advent of Code 2023](https://adventofcode.com/2023) | [Solutions](2023/README.md) | 50⭐ | 25 | 11 | 1
4546
[Advent of Code 2022](https://adventofcode.com/2022) | [Solutions](2022/README.md) | 50⭐ | 24 | 18 |
4647
[Advent of Code 2021](https://adventofcode.com/2021) | [Solutions](2021/README.md) | 50⭐ | 24 | 12 |

0 commit comments

Comments
Β (0)