Add 2024 Quest 11

This commit is contained in:
Chris Alge 2024-11-19 11:03:09 +01:00
parent f7f796ed93
commit 9ad4df7eca
9 changed files with 291 additions and 0 deletions

View file

@ -0,0 +1,6 @@
[package]
name = "day11_biological_warfare"
version = "0.1.0"
edition = "2021"
[dependencies]

View file

@ -0,0 +1,34 @@
Part I
The next tournament competition takes place in a very peculiar location, the Biological Weapons District. Unconventional and cunning warfare methods are a specialty of the Knights of the Order.
One such method involves breeding Omnivorous Termites. Their strength lies not only in their ability to eat literally anything, including the enemy's weaponry, but also in their rapid population growth rate. Just a few suitable specimens sent near the enemy's castle can ensure that, within a short time, no trace of it will remain.
However, for breeding purposes, it is crucial to consciously control the population size. Through years of work, the life cycle of the entire species has been identified. All individuals have been categorized, and for each category, the appearance of the next generation has been determined.
Each individual converts into a new generation every day. You have noted down all conversions (your notes). The new individuals are also capable of converting the following day, so the overall population can grow extremely quickly, but it can also be very accurately predicted. This is precisely what the next tournament task involves. You start with a single A category termite, and you have to calculate the overall population after 4 days, based on the conversion notes.
Example based on the following notes:
A:B,C
B:C,A
C:A
You start with a single A termite.
After the first day, it converts into B and C category termites, so the population looks as follows: B,C
After the second day, each termite converts again, so the population grows to 3: C,A,A
After the third day, the population counts 5 termites: A,B,C,B,C, and after the fourth day, 8 termites: B,C,C,A,A,C,A,A.
What will be the termite population count on the 4th day?
Part II
The second round takes place in an even more hostile environment, the Infestation Lab. This time, however, the Knights have perfected their Omnivorous Termites, and they come in a new strain with distinct conversion patterns. These new termites, starting from a single category Z termite, grow in a completely different way than the previous strain, but the general rules remain the same. Your goal is to predict the population count on the 10th day.
What will be the termite population count on the 10th day?
Part III
The final round presents a new, intricate challenge. A newly engineered strain of termites has been developed for an upcoming combat mission. The knights must now investigate how the initial termite types affect the overall population after 20 days of growth.
Your task is to determine which initial termite types will yield the largest and smallest populations on the 20th day, and then calculate the difference between these two population sizes.
Example based on the following notes:
A:B,C
B:C,A,A
C:A
Starting with a single A termite results in a population of 330205 on day 20.
Starting with a single B termite results in a population of 444092 on day 20.
Starting with a single C termite results in a population of 175277 on day 20.
You can calculate the population difference by subtracting the smallest number from the largest: 444092 - 175277 = 268815
What is the population difference on the 20th day?

View file

@ -0,0 +1,116 @@
use core::fmt::Display;
use std::collections::HashMap;
#[derive(Debug, PartialEq, Eq)]
pub enum ParseError<'a> {
LineMalformed(&'a str),
}
impl Display for ParseError<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::LineMalformed(e) => write!(f, "Unable to parse malformed line: {e}\nShould be of format:\nA:B,C,D"),
}
}
}
type Termite=usize;
fn try_cycles_from(input: &str) -> Result<(Vec<Vec<Termite>>, HashMap<&str, Termite>), ParseError> {
let mut names = HashMap::new();
let mut res = Vec::new();
for line in input.lines() {
if let Some((from, to)) = line.split_once(':') {
let next_idx = names.len();
let from_idx = *names.entry(from).or_insert(next_idx);
while res.len() <= from_idx {
res.push(Vec::new());
}
res[from_idx] = to
.split(',')
.map(|name| {
let next_idx = names.len();
*names.entry(name).or_insert(next_idx)
}).collect();
} else {
return Err(ParseError::LineMalformed(line));
}
}
Ok((res, names))
}
fn reproduce(cycles: &[Vec<Termite>], population: &mut Vec<usize>, days: usize) {
let mut next_gen = Vec::with_capacity(cycles.len());
(0..days).for_each(|_| {
next_gen = vec![0; cycles.len()];
population.iter().enumerate().for_each(|(category, count)| {
let children = &cycles[category];
children.iter().for_each(|child| {
next_gen[*child] += count;
});
});
std::mem::swap(population, &mut next_gen);
next_gen.clear();
});
}
pub fn run(input: &str, part: usize) -> Result<usize, ParseError> {
let (cycles, names) = try_cycles_from(input)?;
let mut termites = vec![0; cycles.len()];
match part {
1 => {
termites[*names.get("A").unwrap()] = 1;
reproduce(&cycles, &mut termites, 4);
Ok(termites.iter().sum())
},
2 => {
termites[*names.get("Z").unwrap()] = 1;
reproduce(&cycles, &mut termites, 10);
Ok(termites.iter().sum())
},
3 => {
let mut low = usize::MAX;
let mut high = usize::MIN;
(0..cycles.len()).for_each(|c| {
termites = vec![0; cycles.len()];
termites[c] = 1;
reproduce(&cycles, &mut termites, 20);
let population = termites.iter().sum();
low = low.min(population);
high = high.max(population);
});
Ok(high-low)
},
_ => panic!("Illegal part number"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_sample() {
let expected = [8, 144, 268815];
for part in 1..=expected.len() {
let sample_input = read_file(&format!("tests/sample{part}"));
assert_eq!(run(&sample_input, part), Ok(expected[part-1]));
}
}
#[test]
fn test_challenge() {
let expected = [41, 213729, 896125189572];
for part in 1..=expected.len() {
let challenge_input = read_file(&format!("tests/challenge{part}"));
assert_eq!(run(&challenge_input, part), Ok(expected[part-1]));
}
}
}

View file

@ -0,0 +1,6 @@
A:O,Y,E
I:U,U
O:E,I,E
E:O,I
Y:U,O
U:I,I,A

View file

@ -0,0 +1,20 @@
J:M,D,C,B
M:H,W,T,B
S:F,M,C,T
Q:V,G,C
R:Q,J,G,Z
K:M,M,B
T:R,G,F,M
W:X,R,F
L:S,W,W
H:G,V,G
V:Q,B,J
D:W,N,T
N:F,C,G
G:K,L,H
F:L,R,V
B:G,J,H,X
C:D,P,H,R
P:T,C,Q,Q
X:W,G,M,H
Z:Q,L,L,H

View file

@ -0,0 +1,100 @@
KQW:JSC,JGW,WMZ
PRV:KCG,NQJ,KRS
RLJ:LMB,MFD,WMC,KTG,NPF
CTT:WNW,RLS,FTS,SVZ,RLJ
SRJ:KJS,MLJ,LMB,QSS,DBQ
MFD:SNG,FMQ,GZR,WMC,KJS
DJH:RGL,DJH,PGB,CNQ,WMZ
JRT:RTJ,PNB,SRJ
QTD:WSR,FWH,MSP,LPH,RGL
VGX:KQW,WMZ,PGB,WMC,DHZ
KNF:JWR,QSS,KRS
NSQ:TCC,PNC,QSS
SKR:GXP,CWP,TCC
MLJ:NXZ,VGS,BWR,PGB,KQW
SSP:KTL,CWP,HNZ
PGB:JWR,RTJ,PFP
HNZ:NPF,NDC,FZT
TWC:HGD,MJR,NMF
WSR:VGV,KTL,HNZ,RLS,RTJ
CWP:MLJ,HNZ,WMV
BWR:KVX,RGL,WMV
DBQ:NXZ,FZT,VGS,NXZ,KJS
RXB:QTD,NQJ,RTJ
WRQ:NRW,KTG,KVX,KVX,SPC
JCT:SVZ,NMF,TWC
KRS:LMB,PGF,JFB,FNW,NDC
NLB:RZF,VGH,NRW
WNW:QSS,QTD,NSZ,MJR,WSR
BBX:TWR,WRQ,MLJ
FWD:PGF,LPH,LQT
QSS:RLJ,FFZ,FNW,SNG,FWD
KTL:JSC,SRJ,NDC
FNW:DBQ,HGD,NSQ,NSQ,MJR
KJS:QGG,JWR,RLS,MSP,VGS
PGF:CNQ,NRW,XMM
BSW:VFR,PGB,RLJ,KCG,TMT
DHZ:JFV,BVF,JRT,WCS,HGD
MSP:NSQ,PRV,NQJ
PNB:NPF,QQP,KJS,NPF,DBQ
RGL:KVX,TWC,FNW,TMT,SRJ
FFZ:VGX,XLN,NPF,GDQ,JFV
SVZ:KQW,VGH,WMC,MLJ,WMZ
LQT:RXB,QSS,MJR,TMT,FFZ
FZT:SVZ,WRQ,VLT,KQW,PNB
FMQ:JWR,GXP,VGH
WMC:JFV,RXB,XMM,JSC,SVZ
PNC:TWC,WNW,NMF
LMB:FMQ,VGX,NSZ,CNQ,PFP
WCS:JSC,PNC,GZR
TWR:NQJ,NXQ,SNG
HGD:TWR,RLS,HNZ
XMM:SNG,VLT,KNF,WNW,WMV
WMZ:SPC,MND,KNF,NDC,NRW
JSC:QTD,RGL,KGW,JWR,NQJ
JWR:NXZ,MND,PGF
NMF:KVX,NQJ,FZT
RTJ:GDQ,LMB,RLS
MJR:FZT,FMQ,BVF,LQT,QQP
KTG:KTL,MND,WRQ,VGX,PNC
NQJ:WCS,KTG,MFD,NPF,CNQ
VGS:KTL,QSS,TCC,FNW,NRW
SNG:BVF,NQJ,WMZ
NXQ:PFP,JFV,HGD,NLB,RTJ
KCG:WSR,VGH,NDC,CWP,MSP
NXZ:FMQ,RXB,RXB,TWR,FMQ
KGW:FNW,MFD,GXP
FTS:GXP,JSC,KQW,QTD,NSQ
NPF:KVX,TWC,FNW
BTX:FWH,WSR,BWR
NRW:FMQ,QQP,MJR,NPF,RXB
PWG:RLS,JRT,SNG
MND:BTX,GXP,WMV
FWH:NWQ,NPF,MJR
KVX:JRT,XLN,BVF
TCC:BBX,RZF,PRV
NWQ:KNF,RGL,KRS,MSP,NXZ
LPH:NXZ,GDQ,FZT
GDQ:TMT,NRW,NXQ
VGH:KNF,LQT,DBQ
PFP:JCT,NMF,PNC,NDC,PGB
VFR:NWQ,NDC,FZT,LQT,JWR
VLT:GXP,VGV,FWH,NDC,BVF
RLS:SSP,HGD,NXZ
NSZ:QGG,KCG,KTL
TMT:XLN,KNF,LPH
JFV:WMV,WRQ,PGF,XLN,DBQ
VGV:RLS,DJH,FWD,KRS,WMZ
JGW:DJH,FZT,SRJ
NDC:TMT,PGB,FMQ
GXP:XLN,BSW,LPH
CNQ:MND,QGG,NLB,TCC,MND
WMV:BWR,NPF,WRQ,QSS,JGW
JFB:VGH,KNF,KQW,QGG,WRQ
RZF:VGV,VLT,BBX,JGW,WNW
SPC:BSW,PRV,SSP
GZR:KNF,PWG,WNW
BVF:KRS,KVX,WCS,JWR,VFR
QGG:CNQ,QTD,VGS
QQP:JFB,RXB,JWR,RLS,MLJ
XLN:GXP,JCT,NSZ

View file

@ -0,0 +1,3 @@
A:B,C
B:C,A
C:A

View file

@ -0,0 +1,3 @@
Z:B,C
B:C,Z
C:Z

View file

@ -0,0 +1,3 @@
A:B,C
B:C,A,A
C:A