mirror of
https://github.com/Smaug123/advent-of-code-2021
synced 2025-10-05 03:58:39 +00:00
Day 14 (#15)
This commit is contained in:
57
Cargo.lock
generated
57
Cargo.lock
generated
@@ -60,9 +60,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"textwrap",
|
||||
@@ -157,7 +157,7 @@ checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"itoa 0.4.8",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
@@ -206,6 +206,13 @@ dependencies = [
|
||||
"criterion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day_14"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day_16"
|
||||
version = "0.1.0"
|
||||
@@ -292,9 +299,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.1"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
|
||||
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
@@ -305,6 +312,12 @@ version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.55"
|
||||
@@ -322,9 +335,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.108"
|
||||
version = "0.2.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -343,9 +356,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
@@ -405,9 +418,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.32"
|
||||
version = "1.0.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -478,9 +491,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
@@ -505,9 +518,9 @@ checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.130"
|
||||
version = "1.0.131"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||
checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
@@ -521,9 +534,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.130"
|
||||
version = "1.0.131"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||
checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -532,20 +545,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.71"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "063bf466a64011ac24040a49009724ee60a57da1b437617ceb32e53ad61bfb19"
|
||||
checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"itoa 1.0.1",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.81"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
|
||||
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@@ -13,5 +13,6 @@ members = [
|
||||
"day_11",
|
||||
"day_12",
|
||||
"day_13",
|
||||
"day_14",
|
||||
"day_16",
|
||||
]
|
||||
|
15
day_14/Cargo.toml
Normal file
15
day_14/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "day_14"
|
||||
version = "0.1.0"
|
||||
authors = ["Smaug123 <patrick+github@patrickstevens.co.uk>"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
harness = false
|
19
day_14/benches/bench.rs
Normal file
19
day_14/benches/bench.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use day_14::day_14::{input, part_1, part_2};
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let input = input();
|
||||
c.bench_function("day 14 part 1", |b| {
|
||||
b.iter(|| {
|
||||
black_box(part_1(&input));
|
||||
})
|
||||
});
|
||||
c.bench_function("day 14 part 2", |b| {
|
||||
b.iter(|| {
|
||||
black_box(part_2(&input));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
30
day_14/day14.m
Normal file
30
day_14/day14.m
Normal file
@@ -0,0 +1,30 @@
|
||||
(* ::Package:: *)
|
||||
|
||||
parse[s_]:=With[{lines=StringSplit[s,"\n"]},
|
||||
{lines[[1]],With[{sides=StringSplit[#," -> "]},Characters[First@sides]->{{StringTake[First@sides,{1}],Last@sides},{Last@sides,StringTake[First@sides,{2}]}}]&/@lines[[3;;]]}]
|
||||
|
||||
{start,rules}=parse@StringTrim@ReadString[StringJoin[NotebookDirectory[], "/input.txt"]];
|
||||
|
||||
|
||||
(* ::Text:: *)
|
||||
(*Part 1*)
|
||||
|
||||
|
||||
step[rules_,chars_]:=Flatten[chars/.rules,1]
|
||||
|
||||
|
||||
With[{l=SortBy[Tally[Join[StringTake[start,{{1},{-1}}],Flatten@Nest[step[rules,#]&,Partition[Characters@start,2,1],10]]],Last]},
|
||||
Quotient[l[[-1]],2]-Quotient[l[[1]],2]
|
||||
][[2]]
|
||||
|
||||
|
||||
(* ::Text:: *)
|
||||
(*Part 2*)
|
||||
|
||||
|
||||
step[rules_,pairs_]:={#[[1,1]],Total[Last/@#]}&/@GatherBy[Flatten[Thread/@(pairs/.rules),1],First]
|
||||
|
||||
|
||||
With[{l=Cases[Total@Flatten[{Times@@@Nest[step[rules,#]&,Tally@Partition[Characters@start,2,1],40],StringTake[start,{{1},{-1}}]}],_Integer,All]},
|
||||
(Max[l]-Min[l])/2
|
||||
]
|
102
day_14/input.txt
Normal file
102
day_14/input.txt
Normal file
@@ -0,0 +1,102 @@
|
||||
SHHNCOPHONHFBVNKCFFC
|
||||
|
||||
HH -> K
|
||||
PS -> P
|
||||
BV -> H
|
||||
HB -> H
|
||||
CK -> F
|
||||
FN -> B
|
||||
PV -> S
|
||||
KK -> F
|
||||
OF -> C
|
||||
SF -> B
|
||||
KB -> S
|
||||
HO -> O
|
||||
NH -> N
|
||||
ON -> V
|
||||
VF -> K
|
||||
VP -> K
|
||||
PH -> K
|
||||
FF -> V
|
||||
OV -> N
|
||||
BO -> K
|
||||
PO -> S
|
||||
CH -> N
|
||||
FO -> V
|
||||
FB -> H
|
||||
FV -> N
|
||||
FK -> S
|
||||
VC -> V
|
||||
CP -> K
|
||||
CO -> K
|
||||
SV -> N
|
||||
PP -> B
|
||||
BS -> P
|
||||
VS -> C
|
||||
HV -> H
|
||||
NN -> F
|
||||
NK -> C
|
||||
PC -> V
|
||||
HS -> S
|
||||
FS -> S
|
||||
OB -> S
|
||||
VV -> N
|
||||
VO -> P
|
||||
KV -> F
|
||||
SK -> O
|
||||
BC -> P
|
||||
BP -> F
|
||||
NS -> P
|
||||
SN -> S
|
||||
NC -> N
|
||||
FC -> V
|
||||
CN -> S
|
||||
OK -> B
|
||||
SC -> N
|
||||
HN -> B
|
||||
HP -> B
|
||||
KP -> B
|
||||
CB -> K
|
||||
KF -> C
|
||||
OS -> B
|
||||
BH -> O
|
||||
PN -> K
|
||||
VN -> O
|
||||
KH -> F
|
||||
BF -> H
|
||||
HF -> K
|
||||
HC -> P
|
||||
NP -> H
|
||||
KO -> K
|
||||
CF -> H
|
||||
BK -> O
|
||||
OH -> P
|
||||
SO -> F
|
||||
BB -> F
|
||||
VB -> K
|
||||
SP -> O
|
||||
SH -> O
|
||||
PK -> O
|
||||
HK -> P
|
||||
CC -> V
|
||||
NB -> O
|
||||
NV -> F
|
||||
OO -> F
|
||||
VK -> V
|
||||
FH -> H
|
||||
SS -> C
|
||||
NO -> P
|
||||
CS -> H
|
||||
BN -> V
|
||||
FP -> N
|
||||
OP -> N
|
||||
PB -> P
|
||||
OC -> O
|
||||
SB -> V
|
||||
VH -> O
|
||||
KS -> B
|
||||
PF -> N
|
||||
KN -> H
|
||||
NF -> N
|
||||
CV -> K
|
||||
KC -> B
|
213
day_14/src/lib.rs
Normal file
213
day_14/src/lib.rs
Normal file
@@ -0,0 +1,213 @@
|
||||
pub mod day_14 {
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Data {
|
||||
start: char,
|
||||
end: char,
|
||||
rules: HashMap<(char, char), char>,
|
||||
}
|
||||
|
||||
pub(crate) fn parse(s: &str) -> (Data, HashMap<(char, char), u64>) {
|
||||
let mut lines = s.split('\n');
|
||||
let start = lines.next().unwrap();
|
||||
match lines.next().unwrap() {
|
||||
"" => {}
|
||||
s => panic!("Expected empty line, got {}", s),
|
||||
}
|
||||
|
||||
let mut map = HashMap::new();
|
||||
for line in lines {
|
||||
let mut iter = line.split(' ');
|
||||
let pair = iter.next().unwrap();
|
||||
match iter.next().unwrap() {
|
||||
"->" => {}
|
||||
s => panic!("Expected arrow, got {}", s),
|
||||
}
|
||||
let middle = iter.next().unwrap();
|
||||
match iter.next() {
|
||||
None => {}
|
||||
Some(next) => panic!("Expected end of line, got {}", next),
|
||||
}
|
||||
|
||||
let mut chars = pair.chars();
|
||||
let p1 = chars.next().unwrap();
|
||||
let p2 = chars.next().unwrap();
|
||||
match chars.next() {
|
||||
None => {}
|
||||
Some(_) => panic!("Expected pair of chars, got {}", pair),
|
||||
}
|
||||
|
||||
let mut chars = middle.chars();
|
||||
let insert = chars.next().unwrap();
|
||||
match chars.next() {
|
||||
None => {}
|
||||
Some(_) => panic!("Expected single char, got {}", middle),
|
||||
}
|
||||
map.insert((p1, p2), insert);
|
||||
}
|
||||
|
||||
let mut pairs = HashMap::new();
|
||||
let mut iter = start.chars();
|
||||
let start = iter.next().unwrap();
|
||||
|
||||
let mut prev = start;
|
||||
|
||||
for next in iter {
|
||||
pairs
|
||||
.entry((prev, next))
|
||||
.and_modify(|x| *x += 1)
|
||||
.or_insert(1);
|
||||
prev = next;
|
||||
}
|
||||
|
||||
(
|
||||
Data {
|
||||
start,
|
||||
end: prev,
|
||||
rules: map,
|
||||
},
|
||||
pairs,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn input() -> (Data, HashMap<(char, char), u64>) {
|
||||
parse(include_str!("../input.txt"))
|
||||
}
|
||||
|
||||
fn run(
|
||||
data: &Data,
|
||||
pairs: &HashMap<(char, char), u64>,
|
||||
steps: u8,
|
||||
) -> HashMap<(char, char), u64> {
|
||||
let mut pairs = pairs.clone();
|
||||
for _ in 0..steps {
|
||||
let mut after_step = HashMap::new();
|
||||
for (&(c1, c2), &count) in pairs.iter() {
|
||||
match data.rules.get(&(c1, c2)) {
|
||||
None => {
|
||||
after_step
|
||||
.entry((c1, c2))
|
||||
.and_modify(|x| *x += count)
|
||||
.or_insert(count);
|
||||
}
|
||||
Some(&middle) => {
|
||||
after_step
|
||||
.entry((c1, middle))
|
||||
.and_modify(|x| *x += count)
|
||||
.or_insert(count);
|
||||
after_step
|
||||
.entry((middle, c2))
|
||||
.and_modify(|x| *x += count)
|
||||
.or_insert(count);
|
||||
}
|
||||
};
|
||||
}
|
||||
pairs = after_step;
|
||||
}
|
||||
|
||||
pairs
|
||||
}
|
||||
|
||||
fn min_max<I, T, U>(mut iter: I, f: fn(T) -> U) -> Option<(T, T)>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Copy,
|
||||
U: Ord + Copy,
|
||||
{
|
||||
let mut min_entry = iter.next()?;
|
||||
let mut max_entry = min_entry;
|
||||
let mut min = f(min_entry);
|
||||
let mut max = min;
|
||||
for entry in iter {
|
||||
let f_val = f(entry);
|
||||
if f_val < min {
|
||||
min = f_val;
|
||||
min_entry = entry;
|
||||
} else if f_val > max {
|
||||
max = f_val;
|
||||
max_entry = entry;
|
||||
}
|
||||
}
|
||||
Some((min_entry, max_entry))
|
||||
}
|
||||
|
||||
fn count(map: &HashMap<(char, char), u64>, start: char, end: char) -> HashMap<char, u64> {
|
||||
let mut counts = HashMap::new();
|
||||
for (&(c1, c2), &count) in map.iter() {
|
||||
counts
|
||||
.entry(c1)
|
||||
.and_modify(|i| *i += count)
|
||||
.or_insert(count);
|
||||
counts
|
||||
.entry(c2)
|
||||
.and_modify(|i| *i += count)
|
||||
.or_insert(count);
|
||||
}
|
||||
*counts.get_mut(&start).unwrap() += 1;
|
||||
*counts.get_mut(&end).unwrap() += 1;
|
||||
|
||||
counts
|
||||
}
|
||||
|
||||
pub fn part_1(data: &(Data, HashMap<(char, char), u64>)) -> u64 {
|
||||
let map = run(&data.0, &data.1, 10);
|
||||
let counts = count(&map, data.0.start, data.0.end);
|
||||
match min_max(counts.iter(), |x| x.1) {
|
||||
Some((min, max)) => max.1 / 2 - min.1 / 2,
|
||||
None => panic!("Expected a nonempty map"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn part_2(data: &(Data, HashMap<(char, char), u64>)) -> u64 {
|
||||
let map = run(&data.0, &data.1, 40);
|
||||
let counts = count(&map, data.0.start, data.0.end);
|
||||
match min_max(counts.iter(), |x| x.1) {
|
||||
Some((min, max)) => max.1 / 2 - min.1 / 2,
|
||||
None => panic!("Expected a nonempty map"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::day_14::*;
|
||||
|
||||
static TEST_INPUT: &str = "NNCB
|
||||
|
||||
CH -> B
|
||||
HH -> N
|
||||
CB -> H
|
||||
NH -> C
|
||||
HB -> C
|
||||
HC -> B
|
||||
HN -> C
|
||||
NN -> C
|
||||
BH -> H
|
||||
NC -> B
|
||||
NB -> B
|
||||
BN -> B
|
||||
BB -> N
|
||||
BC -> B
|
||||
CC -> N
|
||||
CN -> C";
|
||||
|
||||
#[test]
|
||||
fn part1_known() {
|
||||
let data = parse(TEST_INPUT);
|
||||
assert_eq!(part_1(&data), 1588);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_known() {
|
||||
let data = parse(TEST_INPUT);
|
||||
assert_eq!(part_2(&data), 2188189693529);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_day_14() {
|
||||
let input = input();
|
||||
assert_eq!(part_1(&input), 2549);
|
||||
assert_eq!(part_2(&input), 2516901104210);
|
||||
}
|
||||
}
|
7
day_14/src/main.rs
Normal file
7
day_14/src/main.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use day_14::day_14::{input, part_1, part_2};
|
||||
|
||||
fn main() {
|
||||
let input = input();
|
||||
println!("part 1 => {}", part_1(&input));
|
||||
println!("part 2 => {}", part_2(&input));
|
||||
}
|
Reference in New Issue
Block a user