day 12
This commit is contained in:
parent
df55d78dec
commit
dffb8a8673
174
2021/src/day12.rs
Normal file
174
2021/src/day12.rs
Normal file
@ -0,0 +1,174 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::str::FromStr;
|
||||
|
||||
use color_eyre::eyre;
|
||||
use eyre::eyre;
|
||||
|
||||
use crate::lib::IterExt;
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
enum Size {
|
||||
Big,
|
||||
Small,
|
||||
}
|
||||
|
||||
|
||||
// this is a terrible way to do it, because it's completely tied to
|
||||
// the dataset and needs to be redone in order to e.g. use a sample
|
||||
// dataset. A better way would be to use [char; 2] to represent each
|
||||
// cave and supply a method that computes size from letter case.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[allow(non_camel_case_types)]
|
||||
enum Tag {
|
||||
start,
|
||||
de,
|
||||
DN,
|
||||
jv,
|
||||
MU,
|
||||
oa,
|
||||
OC,
|
||||
rj,
|
||||
vd,
|
||||
VP,
|
||||
WK,
|
||||
yb,
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct Cave {
|
||||
tag: Tag,
|
||||
size: Size,
|
||||
}
|
||||
|
||||
|
||||
impl FromStr for Cave {
|
||||
type Err = eyre::Report;
|
||||
fn from_str(s: &str) -> eyre::Result<Self> {
|
||||
let cave = match s {
|
||||
"start" => Cave {tag: Tag::start, size: Size::Small},
|
||||
"de" => Cave {tag: Tag::de, size: Size::Small},
|
||||
"DN" => Cave {tag: Tag::DN, size: Size::Big},
|
||||
"jv" => Cave {tag: Tag::jv, size: Size::Small},
|
||||
"MU" => Cave {tag: Tag::MU, size: Size::Big},
|
||||
"oa" => Cave {tag: Tag::oa, size: Size::Small},
|
||||
"OC" => Cave {tag: Tag::OC, size: Size::Big},
|
||||
"rj" => Cave {tag: Tag::rj, size: Size::Small},
|
||||
"vd" => Cave {tag: Tag::vd, size: Size::Small},
|
||||
"VP" => Cave {tag: Tag::VP, size: Size::Big},
|
||||
"WK" => Cave {tag: Tag::WK, size: Size::Big},
|
||||
"yb" => Cave {tag: Tag::yb, size: Size::Small},
|
||||
"end" => Cave {tag: Tag::end, size: Size::Small},
|
||||
_ => return Err(eyre!("Invalid cave tag: {}", s)),
|
||||
};
|
||||
Ok(cave)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type CaveMap = HashMap<Tag, Vec<Cave>>;
|
||||
|
||||
|
||||
struct Path {
|
||||
order: Vec<Cave>,
|
||||
visited: HashSet<Cave>,
|
||||
doubled: Option<Cave>
|
||||
}
|
||||
|
||||
impl Path {
|
||||
fn new() -> Self {
|
||||
Path {
|
||||
order: vec![Cave {tag: Tag::start, size: Size::Small}],
|
||||
visited: HashSet::new(),
|
||||
doubled: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, cave: Cave) {
|
||||
self.order.push(cave);
|
||||
if cave.size == Size::Small {
|
||||
let inserted = self.visited.insert(cave);
|
||||
if !inserted { // value was already present in set
|
||||
self.doubled = Some(cave);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pop(&mut self) {
|
||||
let cave = self.order.pop().unwrap();
|
||||
if cave.size == Size::Small {
|
||||
match self.doubled {
|
||||
Some(d) if d == cave => {self.doubled = None},
|
||||
_ => {self.visited.remove(&cave);}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn last(&self) -> Option<&Cave> {
|
||||
self.order.last()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn load(data: &str) -> eyre::Result<CaveMap> {
|
||||
let mut map = HashMap::new();
|
||||
for line in data.lines() {
|
||||
let (a, b) = line.split('-').take_pair()?;
|
||||
let cave_a = a.parse::<Cave>()?;
|
||||
let cave_b = b.parse::<Cave>()?;
|
||||
|
||||
let neighbors_a = map.entry(cave_a.tag).or_insert_with(|| vec![]);
|
||||
neighbors_a.push(cave_b);
|
||||
|
||||
let neighbors_b = map.entry(cave_b.tag).or_insert_with(|| vec![]);
|
||||
neighbors_b.push(cave_a);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
|
||||
fn can_visit_part1(path: &Path, cave: &Cave) -> bool {
|
||||
if cave.size == Size::Big {true}
|
||||
else if cave.tag == Tag::start {false}
|
||||
else if path.visited.contains(cave) {false}
|
||||
else {true}
|
||||
}
|
||||
|
||||
fn can_visit_part2(path: &Path, cave: &Cave) -> bool {
|
||||
if cave.size == Size::Big {true}
|
||||
else if cave.tag == Tag::start {false}
|
||||
else if path.visited.contains(cave) && path.doubled.is_some() {false}
|
||||
else {true}
|
||||
}
|
||||
|
||||
|
||||
fn search<F>(map: &CaveMap, path: &mut Path, can_visit: F) -> Vec<Vec<Cave>>
|
||||
where F: Fn(&Path, &Cave) -> bool + Copy
|
||||
{
|
||||
let mut result = Vec::new();
|
||||
for neighbor in &map[&path.last().unwrap().tag] {
|
||||
if neighbor.tag == Tag::end {
|
||||
path.push(*neighbor);
|
||||
result.push(path.order.clone());
|
||||
path.pop();
|
||||
}
|
||||
else if can_visit(path, neighbor) {
|
||||
path.push(*neighbor);
|
||||
let child_paths = search(map, path, can_visit);
|
||||
result.extend(child_paths);
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
pub fn run(data: &str) -> eyre::Result<(usize, usize)> {
|
||||
let map = load(data)?;
|
||||
let one = search(&map, &mut Path::new(), can_visit_part1);
|
||||
let two = search(&map, &mut Path::new(), can_visit_part2);
|
||||
|
||||
Ok((one.len(), two.len()))
|
||||
}
|
@ -133,9 +133,8 @@ impl Iterator for NeighborCoords {
|
||||
let row_offset = self.start_row + rel_row;
|
||||
let col_offset = self.start_col + rel_col;
|
||||
// the "neighbor window" is off by 1, so we make the comparisons off by 1 as well
|
||||
if
|
||||
(row_offset > 0 && row_offset <= self.num_rows
|
||||
&& col_offset > 0 && col_offset <= self.num_cols)
|
||||
if row_offset > 0 && row_offset <= self.num_rows
|
||||
&& col_offset > 0 && col_offset <= self.num_cols
|
||||
{
|
||||
Some((row_offset - 1, col_offset - 1))
|
||||
}
|
||||
|
@ -4,14 +4,14 @@ use color_eyre::eyre;
|
||||
|
||||
mod lib;
|
||||
use lib::load;
|
||||
mod day11;
|
||||
mod day12;
|
||||
|
||||
|
||||
fn main() -> eyre::Result<()> {
|
||||
let data = load("data/11.txt")?;
|
||||
let data = load("data/12.txt")?;
|
||||
|
||||
let start = Instant::now();
|
||||
let (one, two) = day11::run(&data)?;
|
||||
let (one, two) = day12::run(&data)?;
|
||||
let (dur, unit) = format_ns(start.elapsed().as_nanos());
|
||||
|
||||
let precision = 2.0 - dur.log10().floor();
|
||||
|
Loading…
x
Reference in New Issue
Block a user