tangled
alpha
login
or
join now
bwc9876.dev
/
advent
0
fork
atom
Advent of Code solutions
0
fork
atom
overview
issues
pulls
pipelines
Day 24 & 25
bwc9876.dev
1 year ago
5a990294
daac2043
verified
This commit was signed with the committer's
known signature
.
bwc9876.dev
SSH Key Fingerprint:
SHA256:DanMEP/RNlSC7pAVbnXO6wzQV00rqyKj053tz4uH5gQ=
+365
-49
4 changed files
expand all
collapse all
unified
split
.gitignore
Cargo.lock
years
2024
src
day_24.rs
day_25.rs
+1
.gitignore
···
8
8
9
9
# Nice one rustc
10
10
rustc-ice*
11
11
+
*.dot
11
12
+25
-38
Cargo.lock
···
42
42
43
43
[[package]]
44
44
name = "console"
45
45
-
version = "0.15.8"
45
45
+
version = "0.15.10"
46
46
source = "registry+https://github.com/rust-lang/crates.io-index"
47
47
-
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
47
47
+
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
48
48
dependencies = [
49
49
"encode_unicode",
50
50
-
"lazy_static",
51
50
"libc",
52
52
-
"unicode-width 0.1.14",
51
51
+
"once_cell",
52
52
+
"unicode-width",
53
53
"windows-sys",
54
54
]
55
55
···
86
86
87
87
[[package]]
88
88
name = "encode_unicode"
89
89
-
version = "0.3.6"
89
89
+
version = "1.0.0"
90
90
source = "registry+https://github.com/rust-lang/crates.io-index"
91
91
-
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
91
91
+
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
92
92
93
93
[[package]]
94
94
name = "indicatif"
···
99
99
"console",
100
100
"number_prefix",
101
101
"portable-atomic",
102
102
-
"unicode-width 0.2.0",
102
102
+
"unicode-width",
103
103
"web-time",
104
104
]
105
105
106
106
[[package]]
107
107
name = "js-sys"
108
108
-
version = "0.3.74"
108
108
+
version = "0.3.76"
109
109
source = "registry+https://github.com/rust-lang/crates.io-index"
110
110
-
checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705"
110
110
+
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
111
111
dependencies = [
112
112
"once_cell",
113
113
"wasm-bindgen",
114
114
]
115
115
116
116
[[package]]
117
117
-
name = "lazy_static"
118
118
-
version = "1.5.0"
119
119
-
source = "registry+https://github.com/rust-lang/crates.io-index"
120
120
-
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
121
121
-
122
122
-
[[package]]
123
117
name = "libc"
124
124
-
version = "0.2.167"
118
118
+
version = "0.2.169"
125
119
source = "registry+https://github.com/rust-lang/crates.io-index"
126
126
-
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
120
120
+
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
127
121
128
122
[[package]]
129
123
name = "log"
···
231
225
232
226
[[package]]
233
227
name = "syn"
234
234
-
version = "2.0.90"
228
228
+
version = "2.0.91"
235
229
source = "registry+https://github.com/rust-lang/crates.io-index"
236
236
-
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
230
230
+
checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035"
237
231
dependencies = [
238
232
"proc-macro2",
239
233
"quote",
···
248
242
249
243
[[package]]
250
244
name = "unicode-width"
251
251
-
version = "0.1.14"
252
252
-
source = "registry+https://github.com/rust-lang/crates.io-index"
253
253
-
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
254
254
-
255
255
-
[[package]]
256
256
-
name = "unicode-width"
257
245
version = "0.2.0"
258
246
source = "registry+https://github.com/rust-lang/crates.io-index"
259
247
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
···
264
252
265
253
[[package]]
266
254
name = "wasm-bindgen"
267
267
-
version = "0.2.97"
255
255
+
version = "0.2.99"
268
256
source = "registry+https://github.com/rust-lang/crates.io-index"
269
269
-
checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c"
257
257
+
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
270
258
dependencies = [
271
259
"cfg-if",
272
260
"once_cell",
···
275
263
276
264
[[package]]
277
265
name = "wasm-bindgen-backend"
278
278
-
version = "0.2.97"
266
266
+
version = "0.2.99"
279
267
source = "registry+https://github.com/rust-lang/crates.io-index"
280
280
-
checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd"
268
268
+
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
281
269
dependencies = [
282
270
"bumpalo",
283
271
"log",
284
284
-
"once_cell",
285
272
"proc-macro2",
286
273
"quote",
287
274
"syn",
···
290
277
291
278
[[package]]
292
279
name = "wasm-bindgen-macro"
293
293
-
version = "0.2.97"
280
280
+
version = "0.2.99"
294
281
source = "registry+https://github.com/rust-lang/crates.io-index"
295
295
-
checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051"
282
282
+
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
296
283
dependencies = [
297
284
"quote",
298
285
"wasm-bindgen-macro-support",
···
300
287
301
288
[[package]]
302
289
name = "wasm-bindgen-macro-support"
303
303
-
version = "0.2.97"
290
290
+
version = "0.2.99"
304
291
source = "registry+https://github.com/rust-lang/crates.io-index"
305
305
-
checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d"
292
292
+
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
306
293
dependencies = [
307
294
"proc-macro2",
308
295
"quote",
···
313
300
314
301
[[package]]
315
302
name = "wasm-bindgen-shared"
316
316
-
version = "0.2.97"
303
303
+
version = "0.2.99"
317
304
source = "registry+https://github.com/rust-lang/crates.io-index"
318
318
-
checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
305
305
+
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
319
306
320
307
[[package]]
321
308
name = "web-time"
···
329
316
330
317
[[package]]
331
318
name = "windows-sys"
332
332
-
version = "0.52.0"
319
319
+
version = "0.59.0"
333
320
source = "registry+https://github.com/rust-lang/crates.io-index"
334
334
-
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
321
321
+
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
335
322
dependencies = [
336
323
"windows-targets",
337
324
]
+296
-6
years/2024/src/day_24.rs
···
1
1
+
use std::collections::{HashMap, HashSet, VecDeque};
1
2
2
2
-
use advent_core::{Day, day_stuff, ex_for_day};
3
3
+
use advent_core::{day_stuff, ex_for_day, Day};
3
4
4
5
pub struct Day24;
5
6
7
7
+
pub type Wires = HashMap<String, bool>;
8
8
+
pub type Gates = HashMap<(String, String, String), Gate>;
9
9
+
10
10
+
fn find_gate<'a>(gates: &'a Gates, lhs: &str, rhs: &str, op: Op) -> Option<&'a Gate> {
11
11
+
gates.get(&(lhs.to_string(), rhs.to_string(), format!("{op:?}")))
12
12
+
}
13
13
+
14
14
+
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
15
15
+
pub enum Op {
16
16
+
And,
17
17
+
Or,
18
18
+
Xor,
19
19
+
}
20
20
+
21
21
+
impl Op {
22
22
+
fn eval(&self, lhs: bool, rhs: bool) -> bool {
23
23
+
match self {
24
24
+
Self::And => lhs & rhs,
25
25
+
Self::Or => lhs | rhs,
26
26
+
Self::Xor => lhs ^ rhs,
27
27
+
}
28
28
+
}
29
29
+
}
30
30
+
31
31
+
#[derive(Debug, Clone)]
32
32
+
pub struct Gate {
33
33
+
lhs: String,
34
34
+
op: Op,
35
35
+
rhs: String,
36
36
+
target: String,
37
37
+
}
38
38
+
39
39
+
impl Gate {
40
40
+
pub fn parse(raw: &str) -> Self {
41
41
+
let mut s = raw.split(" ");
42
42
+
let lhs = s.next().unwrap().to_string();
43
43
+
let op = s.next().unwrap();
44
44
+
let rhs = s.next().unwrap().to_string();
45
45
+
let target = s.skip(1).next().unwrap().to_string();
46
46
+
let op = match op {
47
47
+
"AND" => Op::And,
48
48
+
"OR" => Op::Or,
49
49
+
"XOR" => Op::Xor,
50
50
+
_ => panic!(),
51
51
+
};
52
52
+
Self {
53
53
+
lhs,
54
54
+
rhs,
55
55
+
target,
56
56
+
op,
57
57
+
}
58
58
+
}
59
59
+
60
60
+
pub fn run(&self, wires: &mut Wires) -> bool {
61
61
+
if let (Some(&lhs), Some(&rhs)) = (wires.get(&self.lhs), wires.get(&self.rhs)) {
62
62
+
wires.insert(self.target.clone(), self.op.eval(lhs, rhs));
63
63
+
true
64
64
+
} else {
65
65
+
false
66
66
+
}
67
67
+
}
68
68
+
}
69
69
+
70
70
+
enum AdderTestResult {
71
71
+
Okay(String),
72
72
+
SwapNeeded(String, String),
73
73
+
CompletelyWrong,
74
74
+
End,
75
75
+
}
76
76
+
77
77
+
impl AdderTestResult {
78
78
+
fn unwrap_okay(self) -> String {
79
79
+
match self {
80
80
+
Self::Okay(s) => s,
81
81
+
_ => panic!(),
82
82
+
}
83
83
+
}
84
84
+
}
85
85
+
86
86
+
// The first adder is a half adder and needs to follow this format:
87
87
+
//
88
88
+
// x00, y00 -> AND -> [carry output wire]
89
89
+
// x00, y00 -> XOR -> z00
90
90
+
//
91
91
+
fn test_first_adder(gates: &Gates) -> AdderTestResult {
92
92
+
let x0 = "x00".to_string();
93
93
+
let y0 = "y00".to_string();
94
94
+
if let Some(Gate { target, .. }) = find_gate(gates, &x0, &y0, Op::And) {
95
95
+
if let Some(Gate {
96
96
+
target: z_target, ..
97
97
+
}) = find_gate(gates, &x0, &y0, Op::Xor)
98
98
+
{
99
99
+
if z_target == "z00" {
100
100
+
if target == "z01" {
101
101
+
AdderTestResult::End
102
102
+
} else {
103
103
+
AdderTestResult::Okay(target.to_string())
104
104
+
}
105
105
+
} else {
106
106
+
AdderTestResult::SwapNeeded(target.to_string(), z_target.to_string())
107
107
+
}
108
108
+
} else {
109
109
+
AdderTestResult::CompletelyWrong
110
110
+
}
111
111
+
} else {
112
112
+
AdderTestResult::CompletelyWrong
113
113
+
}
114
114
+
}
115
115
+
116
116
+
// Full adders must follow this pattern:
117
117
+
//
118
118
+
// 1. x[], y[] -> AND -> [xy_and_target]
119
119
+
// 2. x[], y[] -> XOR -> [xy_xor_target]
120
120
+
// 3. [carry_input], [xy_xor_target] -> XOR -> z[]
121
121
+
// 4. [carry_input], [xy_xor_target] -> AND -> [carry_xy_target]
122
122
+
// 5. [carry_xy_target], [xy_and_target] -> OR -> [carry_output]
123
123
+
//
124
124
+
// Of the gates, the ones that can swap outputs without creating a loop are as follows:
125
125
+
// A. #1 & #2
126
126
+
// B. #3 & #5
127
127
+
// C. #3 & #4
128
128
+
// D. #1 & #3
129
129
+
//
130
130
+
// These ones would result in insane or same output:
131
131
+
// #1 & #4, this actually results in the same output no matter what
132
132
+
// #2 & #3, this will result in #3's own output be its input which is invalid to this problem
133
133
+
// #2 & #4, ^
134
134
+
// #2 & #5, ^
135
135
+
// #4 & #5, ^
136
136
+
//
137
137
+
fn test_adder(num: usize, carry_input: &String, gates: &Gates) -> AdderTestResult {
138
138
+
let x = format!("x{num:02}");
139
139
+
let y = format!("y{num:02}");
140
140
+
let z = format!("z{num:02}");
141
141
+
if let (
142
142
+
Some(Gate {
143
143
+
target: xy_and_target,
144
144
+
..
145
145
+
}),
146
146
+
Some(Gate {
147
147
+
target: xy_xor_target,
148
148
+
..
149
149
+
}),
150
150
+
) = (
151
151
+
find_gate(gates, &x, &y, Op::And),
152
152
+
find_gate(gates, &x, &y, Op::Xor),
153
153
+
) {
154
154
+
if let Some(Gate {
155
155
+
target: z_target, ..
156
156
+
}) = find_gate(gates, &carry_input, xy_xor_target.as_str(), Op::Xor)
157
157
+
{
158
158
+
if *z_target != z {
159
159
+
// We know gate #3 is pointing to the wrong target, as it should be pointing to z[]
160
160
+
// We can now confidently swap z[] and this target
161
161
+
// This covers invalid state B, C, and D
162
162
+
return AdderTestResult::SwapNeeded(z_target.to_string(), z);
163
163
+
}
164
164
+
} else {
165
165
+
// We know that gate #2 has an invalid output, and since the only case that involves #2
166
166
+
// is case A, we know that we're swapped with gate #1, we simply need to return the two
167
167
+
// targets we already have
168
168
+
return AdderTestResult::SwapNeeded(
169
169
+
xy_and_target.to_string(),
170
170
+
xy_xor_target.to_string(),
171
171
+
);
172
172
+
};
173
173
+
174
174
+
// From here we've checked all test cases, we can confidently attempt to find the carry
175
175
+
// output now
176
176
+
let carry_xy_target = &find_gate(&gates, xy_xor_target, &carry_input, Op::And)
177
177
+
.expect("Failed to find carry_xy_target")
178
178
+
.target;
179
179
+
let carry_out = &find_gate(&gates, carry_xy_target, xy_and_target, Op::Or)
180
180
+
.expect("Failed to find carry_out")
181
181
+
.target;
182
182
+
if *carry_out == format!("z{:02}", num + 1) {
183
183
+
AdderTestResult::End
184
184
+
} else {
185
185
+
AdderTestResult::Okay(carry_out.to_string())
186
186
+
}
187
187
+
} else {
188
188
+
AdderTestResult::CompletelyWrong
189
189
+
}
190
190
+
}
191
191
+
192
192
+
fn swap_outputs(gates: &mut Gates, out1: &String, out2: &String) {
193
193
+
gates.values_mut().for_each(|g| {
194
194
+
if g.target == *out1 {
195
195
+
g.target = out2.to_string();
196
196
+
} else if g.target == *out2 {
197
197
+
g.target = out1.to_string();
198
198
+
}
199
199
+
});
200
200
+
}
201
201
+
202
202
+
// 0,1,
203
203
+
// z16,tdv,hnd,z09,z23,bks,nrn,tjp
204
204
+
6
205
impl Day for Day24 {
206
206
+
day_stuff!(24, "", "", (Wires, Gates));
7
207
8
8
-
day_stuff!(24, "", "");
208
208
+
fn part_1((mut wires, gates): Self::Input) -> Option<String> {
209
209
+
let mut all_zs = gates
210
210
+
.values()
211
211
+
.filter(|g| g.target.starts_with('z'))
212
212
+
.map(|g| &g.target)
213
213
+
.collect::<Vec<_>>();
214
214
+
all_zs.sort();
215
215
+
216
216
+
let mut current_zs = HashSet::<&String>::with_capacity(all_zs.len());
217
217
+
218
218
+
let mut queue = gates.values().collect::<VecDeque<_>>();
219
219
+
220
220
+
while let Some(gate) = queue.pop_front()
221
221
+
&& current_zs.len() < all_zs.len()
222
222
+
{
223
223
+
if gate.run(&mut wires) {
224
224
+
if gate.target.starts_with('z') {
225
225
+
current_zs.insert(&gate.target);
226
226
+
}
227
227
+
} else {
228
228
+
queue.push_back(gate);
229
229
+
}
230
230
+
}
231
231
+
232
232
+
let ans = all_zs.into_iter().enumerate().fold(0_usize, |acc, (i, z)| {
233
233
+
let wire = wires.get(z).unwrap();
234
234
+
if *wire {
235
235
+
acc | (1 << i)
236
236
+
} else {
237
237
+
acc
238
238
+
}
239
239
+
});
240
240
+
241
241
+
Some(ans.to_string())
242
242
+
}
243
243
+
244
244
+
fn part_2((_, mut gates): Self::Input) -> Option<String> {
245
245
+
let mut swapped = Vec::with_capacity(8);
246
246
+
let mut current_carry = String::new();
247
247
+
248
248
+
for i in 0.. {
249
249
+
let res = if i == 0 {
250
250
+
test_first_adder(&gates)
251
251
+
} else {
252
252
+
test_adder(i, ¤t_carry, &gates)
253
253
+
};
254
254
+
255
255
+
match res {
256
256
+
AdderTestResult::Okay(new_carry) => {
257
257
+
current_carry = new_carry;
258
258
+
}
259
259
+
AdderTestResult::End => {
260
260
+
break;
261
261
+
}
262
262
+
AdderTestResult::CompletelyWrong => {
263
263
+
panic!("Wrong adder");
264
264
+
}
265
265
+
AdderTestResult::SwapNeeded(l, r) => {
266
266
+
swap_outputs(&mut gates, &l, &r);
267
267
+
current_carry = test_adder(i, ¤t_carry, &gates).unwrap_okay();
268
268
+
swapped.push(l);
269
269
+
swapped.push(r);
270
270
+
}
271
271
+
};
272
272
+
}
9
273
10
10
-
fn part_1(_input: Self::Input) -> Option<String> {
11
11
-
None
274
274
+
swapped.sort();
275
275
+
276
276
+
Some(swapped.join(","))
12
277
}
13
278
14
14
-
fn part_2(_input: Self::Input) -> Option<String> {
15
15
-
None
279
279
+
fn parse_input(input: &str) -> Self::Input {
280
280
+
let (inits, gates) = input.trim().split_once("\n\n").unwrap();
281
281
+
282
282
+
let wires = inits
283
283
+
.lines()
284
284
+
.map(|l| {
285
285
+
let (name, val) = l.split_once(": ").unwrap();
286
286
+
(name.to_string(), val == "1")
287
287
+
})
288
288
+
.collect::<HashMap<_, _>>();
289
289
+
290
290
+
let gates = gates
291
291
+
.lines()
292
292
+
.flat_map(|l| {
293
293
+
let gate = Gate::parse(l);
294
294
+
let op_str = format!("{:?}", gate.op);
295
295
+
[
296
296
+
(
297
297
+
(gate.lhs.clone(), gate.rhs.clone(), op_str.clone()),
298
298
+
gate.clone(),
299
299
+
),
300
300
+
((gate.rhs.clone(), gate.lhs.clone(), op_str), gate),
301
301
+
]
302
302
+
})
303
303
+
.collect::<HashMap<_, _>>();
304
304
+
305
305
+
(wires, gates)
16
306
}
17
307
}
+43
-5
years/2024/src/day_25.rs
···
1
1
+
use std::collections::HashSet;
1
2
2
2
-
use advent_core::{Day, day_stuff, ex_for_day};
3
3
+
use advent_core::{day_stuff, ex_for_day, Day};
4
4
+
use utils::{tiles, upos};
3
5
4
6
pub struct Day25;
5
7
8
8
+
tiles!(Tile, [
9
9
+
'#' => Fill,
10
10
+
'.' => Empty,
11
11
+
]);
12
12
+
13
13
+
type Grid = utils::grid::Grid<Tile>;
14
14
+
6
15
impl Day for Day25 {
16
16
+
day_stuff!(25, "", "", (HashSet<[u8; 5]>, HashSet<[u8; 5]>));
7
17
8
8
-
day_stuff!(25, "", "");
18
18
+
fn part_1((locks, keys): Self::Input) -> Option<String> {
19
19
+
let ans = locks
20
20
+
.into_iter()
21
21
+
.flat_map(|l| {
22
22
+
keys.iter()
23
23
+
.filter(move |k| l.iter().zip(k.iter()).all(|(l, k)| *k <= (5 - *l)))
24
24
+
})
25
25
+
.count();
9
26
10
10
-
fn part_1(_input: Self::Input) -> Option<String> {
11
11
-
None
27
27
+
Some(ans.to_string())
12
28
}
13
29
14
30
fn part_2(_input: Self::Input) -> Option<String> {
15
15
-
None
31
31
+
Some("🥳".to_string())
32
32
+
}
33
33
+
34
34
+
fn parse_input(input: &str) -> Self::Input {
35
35
+
let mut locks = HashSet::new();
36
36
+
let mut keys = HashSet::new();
37
37
+
38
38
+
for grid in input.trim().split("\n\n").map(Grid::parse) {
39
39
+
let code = grid
40
40
+
.iter_cols()
41
41
+
.map(|col| (col.filter(|t| **t == Tile::Fill).count() - 1) as u8)
42
42
+
.collect::<Vec<_>>();
43
43
+
44
44
+
let code = [code[0], code[1], code[2], code[3], code[4]];
45
45
+
46
46
+
if grid.get(upos!(0, 0)).is_some_and(|t| *t == Tile::Fill) {
47
47
+
locks.insert(code);
48
48
+
} else {
49
49
+
keys.insert(code);
50
50
+
}
51
51
+
}
52
52
+
53
53
+
(locks, keys)
16
54
}
17
55
}