This commit is contained in:
Patrick Stevens
2021-12-16 17:55:21 +00:00
committed by GitHub
parent b4ed32b7f7
commit ae25038131
8 changed files with 422 additions and 22 deletions

57
Cargo.lock generated
View File

@@ -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",

View File

@@ -13,5 +13,6 @@ members = [
"day_11",
"day_12",
"day_13",
"day_14",
"day_16",
]

15
day_14/Cargo.toml Normal file
View 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
View 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
View 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
View 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
View 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
View 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));
}