day16 started
This commit is contained in:
parent
96fca503ab
commit
daa8b6b1d0
3 changed files with 140 additions and 5 deletions
|
|
@ -6,8 +6,10 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
const_format = "0.2.31"
|
||||||
itertools = "0.11"
|
itertools = "0.11"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
|
once_cell = "1.18.0"
|
||||||
regex = "1.7"
|
regex = "1.7"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ pub fn read_lines(day_num: usize, file: &str) -> io::Result<Vec<String>> {
|
||||||
Ok(fs::read_to_string(format_path(day_num, file))?
|
Ok(fs::read_to_string(format_path(day_num, file))?
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.with_position()
|
.with_position()
|
||||||
.filter_map(|line| match line {
|
.filter_map(|(pos, line)| match pos {
|
||||||
(itertools::Position::Last, line) if line.is_empty() => None,
|
itertools::Position::Last if line.is_empty() => None,
|
||||||
(_, line) => Some(line.to_string()),
|
_ => Some(line.to_owned()),
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
|
use const_format::concatcp;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
const DAY_NUMBER: usize = 16;
|
const DAY_NUMBER: usize = 16;
|
||||||
|
|
||||||
|
|
@ -9,15 +15,114 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, _lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||||
Ok(ResultType::Nothing)
|
Ok(ResultType::Nothing)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, _lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||||
Ok(ResultType::Nothing)
|
Ok(ResultType::Nothing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum ValveError {
|
||||||
|
#[error("Not an Integer")]
|
||||||
|
NotAnInt(#[from] ParseIntError),
|
||||||
|
|
||||||
|
#[error("Not a valid valve: {0}")]
|
||||||
|
NotAValidValve(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
const ID: &str = "[[:alpha:]]+";
|
||||||
|
const COMMON: &str = concatcp!("^Valve (?<id>", ID, r") has flow rate=(?<rate>\d+); ");
|
||||||
|
const PLURAL_STR: &str = concatcp!(
|
||||||
|
COMMON,
|
||||||
|
"tunnels lead to valves (?<exits>",
|
||||||
|
ID,
|
||||||
|
"(?:, ",
|
||||||
|
ID,
|
||||||
|
")+)$"
|
||||||
|
);
|
||||||
|
const SINGULAR_STR: &str = concatcp!(COMMON, "tunnel leads to valve (?<exits>", ID, ")$");
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
struct Valve {
|
||||||
|
id: String,
|
||||||
|
rate: i64,
|
||||||
|
distances: Vec<(String, i64)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Valve {
|
||||||
|
fn from_regex(regex: &Regex, line: &str) -> Option<Result<Valve, ValveError>> {
|
||||||
|
regex.captures(line).map(|caps| {
|
||||||
|
match (caps.name("id"), caps.name("rate"), caps.name("exits")) {
|
||||||
|
(Some(id), Some(rate), Some(exits)) => {
|
||||||
|
let rate = rate.as_str().parse()?;
|
||||||
|
let distances = exits
|
||||||
|
.as_str()
|
||||||
|
.split(",")
|
||||||
|
.map(|s| (s.trim_start().to_string(), 1))
|
||||||
|
.collect();
|
||||||
|
Ok(Valve {
|
||||||
|
id: id.as_str().to_string(),
|
||||||
|
rate,
|
||||||
|
distances,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(ValveError::NotAValidValve(line.to_string())),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn known_distances(&self) -> usize {
|
||||||
|
self.distances.len() + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn steps_to(&self, to: &str) -> Option<i64> {
|
||||||
|
self.distances
|
||||||
|
.iter()
|
||||||
|
.find_map(|(other, distance)| if other == to { Some(*distance) } else { None })
|
||||||
|
.or_else(|| (to == self.id).then_some(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Valve {
|
||||||
|
type Error = ValveError;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Valve, Self::Error> {
|
||||||
|
static PLURAL: Lazy<Regex> = Lazy::new(|| Regex::new(PLURAL_STR).unwrap());
|
||||||
|
static SINGULAR: Lazy<Regex> = Lazy::new(|| Regex::new(SINGULAR_STR).unwrap());
|
||||||
|
Valve::from_regex(&PLURAL, value)
|
||||||
|
.or_else(|| Valve::from_regex(&SINGULAR, value))
|
||||||
|
.unwrap_or_else(|| Err(ValveError::NotAValidValve(value.to_string())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct System {
|
||||||
|
valves: Vec<Valve>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl System {
|
||||||
|
pub fn get_valve(&self, id: &str) -> Option<&Valve> {
|
||||||
|
self.valves.iter().find(|valve| valve.id == id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.valves.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl System {
|
||||||
|
fn build(lines: &[String]) -> Result<System, ValveError> {
|
||||||
|
Ok(System {
|
||||||
|
valves: lines
|
||||||
|
.iter()
|
||||||
|
.map(|line| Valve::try_from(line.as_str()))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -45,4 +150,32 @@ mod test {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_plural() -> Result<()> {
|
||||||
|
let line = "Valve BB has flow rate=13; tunnels lead to valves CC, AA";
|
||||||
|
let expected = Valve {
|
||||||
|
id: "BB".to_string(),
|
||||||
|
rate: 13,
|
||||||
|
distances: vec![("CC".to_string(), 1), ("AA".to_string(), 1)],
|
||||||
|
};
|
||||||
|
let result = Valve::try_from(line)?;
|
||||||
|
assert_eq!(expected, result);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_singular() -> Result<()> {
|
||||||
|
let line = "Valve HH has flow rate=22; tunnel leads to valve GG";
|
||||||
|
let expected = Valve {
|
||||||
|
id: "HH".to_string(),
|
||||||
|
rate: 22,
|
||||||
|
distances: vec![("GG".to_string(), 1)],
|
||||||
|
};
|
||||||
|
let result = Valve::try_from(line)?;
|
||||||
|
assert_eq!(expected, result);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue