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 row_offset = self.start_row + rel_row;
|
||||||
let col_offset = self.start_col + rel_col;
|
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
|
// the "neighbor window" is off by 1, so we make the comparisons off by 1 as well
|
||||||
if
|
if row_offset > 0 && row_offset <= self.num_rows
|
||||||
(row_offset > 0 && row_offset <= self.num_rows
|
&& col_offset > 0 && col_offset <= self.num_cols
|
||||||
&& col_offset > 0 && col_offset <= self.num_cols)
|
|
||||||
{
|
{
|
||||||
Some((row_offset - 1, col_offset - 1))
|
Some((row_offset - 1, col_offset - 1))
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,14 @@ use color_eyre::eyre;
|
|||||||
|
|
||||||
mod lib;
|
mod lib;
|
||||||
use lib::load;
|
use lib::load;
|
||||||
mod day11;
|
mod day12;
|
||||||
|
|
||||||
|
|
||||||
fn main() -> eyre::Result<()> {
|
fn main() -> eyre::Result<()> {
|
||||||
let data = load("data/11.txt")?;
|
let data = load("data/12.txt")?;
|
||||||
|
|
||||||
let start = Instant::now();
|
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 (dur, unit) = format_ns(start.elapsed().as_nanos());
|
||||||
|
|
||||||
let precision = 2.0 - dur.log10().floor();
|
let precision = 2.0 - dur.log10().floor();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user