implement day 12

This commit is contained in:
Jan Christian Grünhage 2021-12-12 13:00:18 +01:00
parent 49cfe7a7cb
commit d73485111d
4 changed files with 292 additions and 0 deletions

63
src/day_12/mod.rs Normal file
View file

@ -0,0 +1,63 @@
mod model;
mod parsing;
use aoc_runner_derive::{aoc, aoc_generator};
pub use model::{Cave, CaveSystem};
pub use parsing::parse_cave_system;
use yap::IntoTokens;
#[aoc_generator(day12)]
pub fn parse_input(input: &str) -> CaveSystem {
parse_cave_system(&mut input.into_tokens()).unwrap()
}
#[aoc(day12, part1)]
pub fn part1(input: &CaveSystem) -> usize {
input.list_paths(false).len()
}
#[aoc(day12, part2)]
pub fn part2(input: &CaveSystem) -> usize {
input.list_paths(true).len()
}
#[cfg(test)]
mod test {
const EXAMPLE_INPUT: &str = "fs-end
he-DX
fs-he
start-DX
pj-DX
end-zg
zg-sl
zg-pj
pj-he
RW-he
fs-DX
pj-RW
zg-RW
start-pj
he-WI
zg-he
pj-fs
start-RW";
const RESULT_PART_1: usize = 226;
const RESULT_PART_2: usize = 3509;
#[test]
fn part1_example() {
let result = super::part1(&super::parse_input(EXAMPLE_INPUT));
assert_eq!(result, RESULT_PART_1);
}
#[test]
fn part2_example() {
let result = super::part2(&super::parse_input(EXAMPLE_INPUT));
assert_eq!(result, RESULT_PART_2);
}
#[test]
fn parse_example() {
let _example = super::parse_input(EXAMPLE_INPUT);
}
}

156
src/day_12/model.rs Normal file
View file

@ -0,0 +1,156 @@
use std::collections::{HashMap, HashSet};
pub struct CaveSystem {
pub adjacencies: HashMap<Cave, HashSet<Cave>>,
}
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub enum Cave {
Big(String),
Small(String),
Start,
End,
}
impl ToString for Cave {
fn to_string(&self) -> String {
match self {
Cave::Big(cave) => cave.clone(),
Cave::Small(cave) => cave.clone(),
Cave::Start => String::from("start"),
Cave::End => String::from("end"),
}
}
}
impl CaveSystem {
pub fn list_paths(&self, may_visit_twice: bool) -> Vec<Vec<Cave>> {
self.recurse_path(Cave::Start, vec![Cave::Start], may_visit_twice)
}
fn recurse_path(
&self,
current: Cave,
path: Vec<Cave>,
may_visit_twice: bool,
) -> Vec<Vec<Cave>> {
let mut ret_val = Vec::new();
for cave in self.adjacencies.get(&current).unwrap_or(&HashSet::new()) {
match cave {
Cave::Big(_) => {
let mut path = path.clone();
path.push(cave.clone());
ret_val.append(&mut self.recurse_path(cave.clone(), path, may_visit_twice));
}
Cave::Small(_) => {
let mut may_visit_twice = may_visit_twice;
if path.contains(cave) {
if may_visit_twice {
may_visit_twice = false;
} else {
continue;
}
};
let mut path = path.clone();
path.push(cave.clone());
ret_val.append(&mut self.recurse_path(cave.clone(), path, may_visit_twice));
}
Cave::Start => continue,
Cave::End => {
let mut path = path.clone();
path.push(cave.clone());
ret_val.push(path);
}
}
}
ret_val
}
pub fn new(edges: Vec<[Cave; 2]>) -> Result<CaveSystem, &'static str> {
let mut adjacencies: HashMap<Cave, HashSet<Cave>> = Default::default();
for edge in edges {
adjacencies
.entry(edge[0].clone())
.or_default()
.insert(edge[1].clone());
adjacencies
.entry(edge[1].clone())
.or_default()
.insert(edge[0].clone());
}
if !adjacencies.contains_key(&Cave::Start) {
Err("Cave system does not contain start cave")
} else if !adjacencies.contains_key(&Cave::End) {
Err("Cave system does not contain end cave")
} else {
Ok(CaveSystem { adjacencies })
}
}
}
#[cfg(test)]
mod test {
use std::collections::HashSet;
use itertools::Itertools;
#[test]
fn small_example_part_2() {
let input: super::CaveSystem = super::super::parse_input(
"start-A
start-b
A-c
A-b
b-d
A-end
b-end",
);
let paths: HashSet<String> = input
.list_paths(true)
.iter()
.map(|path| path.iter().map(|cave| cave.to_string()).join(","))
.collect();
let expected_paths: HashSet<String> = "start,A,b,A,b,A,c,A,end
start,A,b,A,b,A,end
start,A,b,A,b,end
start,A,b,A,c,A,b,A,end
start,A,b,A,c,A,b,end
start,A,b,A,c,A,c,A,end
start,A,b,A,c,A,end
start,A,b,A,end
start,A,b,d,b,A,c,A,end
start,A,b,d,b,A,end
start,A,b,d,b,end
start,A,b,end
start,A,c,A,b,A,b,A,end
start,A,c,A,b,A,b,end
start,A,c,A,b,A,c,A,end
start,A,c,A,b,A,end
start,A,c,A,b,d,b,A,end
start,A,c,A,b,d,b,end
start,A,c,A,b,end
start,A,c,A,c,A,b,A,end
start,A,c,A,c,A,b,end
start,A,c,A,c,A,end
start,A,c,A,end
start,A,end
start,b,A,b,A,c,A,end
start,b,A,b,A,end
start,b,A,b,end
start,b,A,c,A,b,A,end
start,b,A,c,A,b,end
start,b,A,c,A,c,A,end
start,b,A,c,A,end
start,b,A,end
start,b,d,b,A,c,A,end
start,b,d,b,A,end
start,b,d,b,end
start,b,end"
.lines()
.map(String::from)
.collect();
dbg!(paths.symmetric_difference(&expected_paths));
assert_eq!(paths, expected_paths);
}
}

72
src/day_12/parsing.rs Normal file
View file

@ -0,0 +1,72 @@
use yap::Tokens;
use crate::parsing::{newline, parse_n};
use super::{Cave, CaveSystem};
pub fn parse_cave_system(tokens: &mut impl Tokens<Item = char>) -> Option<CaveSystem> {
let edges = tokens.sep_by(|t| parse_edge(t), |t| newline(t)).collect();
CaveSystem::new(edges).ok()
}
pub fn parse_edge(tokens: &mut impl Tokens<Item = char>) -> Option<[Cave; 2]> {
parse_n(tokens, &|t| parse_cave(t), &|t| t.token('-'))
}
pub fn parse_cave(tokens: &mut impl Tokens<Item = char>) -> Option<Cave> {
yap::one_of!(ts from tokens;
parse_start(ts),
parse_end(ts),
parse_big(ts),
parse_small(ts),
)
}
pub fn parse_start(tokens: &mut impl Tokens<Item = char>) -> Option<Cave> {
tokens.tokens("start".chars()).then(|| Cave::Start)
}
pub fn parse_end(tokens: &mut impl Tokens<Item = char>) -> Option<Cave> {
tokens.tokens("end".chars()).then(|| Cave::End)
}
pub fn parse_big(tokens: &mut impl Tokens<Item = char>) -> Option<Cave> {
let cave: String = tokens.tokens_while(|c| c.is_ascii_uppercase()).collect();
(!cave.is_empty()).then(|| Cave::Big(cave))
}
pub fn parse_small(tokens: &mut impl Tokens<Item = char>) -> Option<Cave> {
let cave: String = tokens.tokens_while(|c| c.is_ascii_lowercase()).collect();
(!cave.is_empty()).then(|| Cave::Small(cave))
}
#[cfg(test)]
mod test {
use yap::IntoTokens;
use super::super::Cave;
#[test]
fn parse_cave() {
assert_eq!(
super::parse_cave(&mut "start".into_tokens()),
Some(Cave::Start)
);
assert_eq!(super::parse_cave(&mut "end".into_tokens()), Some(Cave::End));
assert_eq!(
super::parse_cave(&mut "a".into_tokens()),
Some(Cave::Small(String::from("a")))
);
assert_eq!(
super::parse_cave(&mut "BBBB".into_tokens()),
Some(Cave::Big(String::from("BBBB")))
);
}
#[test]
fn parse_edge() {
assert!(super::parse_edge(&mut "start-DX".into_tokens()).is_some());
assert!(super::parse_edge(&mut "a-DX".into_tokens()).is_some());
assert!(super::parse_edge(&mut "AAA-b".into_tokens()).is_some());
}
}

View file

@ -13,5 +13,6 @@ pub mod day_08;
pub mod day_09;
pub mod day_10;
pub mod day_11;
pub mod day_12;
aoc_lib! { year = 2021 }