Made everything a bit more rustic
This commit is contained in:
parent
7f5b6e03f9
commit
a5f19ecae1
22 changed files with 376 additions and 274 deletions
|
|
@ -5,13 +5,16 @@ fn format_path(day_num: usize, file: &str) -> String {
|
||||||
format!("data/day{day_num:02}/{file}")
|
format!("data/day{day_num:02}/{file}")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_lines(day_num: usize, file: &str) -> io::Result<Vec<String>> {
|
pub fn read_string(day_num: usize, file: &str) -> io::Result<String> {
|
||||||
Ok(fs::read_to_string(format_path(day_num, file))?
|
Ok(fs::read_to_string(format_path(day_num, file))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split_lines<'a>(lines: &'a str) -> impl Iterator<Item = &'a str> + 'a {
|
||||||
|
lines
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.with_position()
|
.with_position()
|
||||||
.filter_map(|(pos, line)| match pos {
|
.filter_map(|(pos, line)| match pos {
|
||||||
itertools::Position::Last if line.is_empty() => None,
|
itertools::Position::Last if line.is_empty() => None,
|
||||||
_ => Some(line.to_owned()),
|
_ => Some(line),
|
||||||
})
|
})
|
||||||
.collect())
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ use std::num::ParseIntError;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
|
|
||||||
pub struct Day;
|
pub struct Day;
|
||||||
|
|
@ -13,13 +15,15 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
|
let lines = split_lines(lines);
|
||||||
let vector = Day::parse(lines)?;
|
let vector = Day::parse(lines)?;
|
||||||
let max = vector.iter().max().ok_or(CalorieError::Empty)?;
|
let max = vector.iter().max().ok_or(CalorieError::Empty)?;
|
||||||
Ok(ResultType::Integer(*max))
|
Ok(ResultType::Integer(*max))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
|
let lines = split_lines(lines);
|
||||||
let vector = Day::parse(lines)?;
|
let vector = Day::parse(lines)?;
|
||||||
let sum = vector.iter().sorted_by(|a, b| Ord::cmp(b, a)).take(3).sum();
|
let sum = vector.iter().sorted_by(|a, b| Ord::cmp(b, a)).take(3).sum();
|
||||||
Ok(ResultType::Integer(sum))
|
Ok(ResultType::Integer(sum))
|
||||||
|
|
@ -27,9 +31,8 @@ impl DayTrait for Day {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Day {
|
impl Day {
|
||||||
fn parse(lines: &[String]) -> Result<Vec<i64>, CalorieError> {
|
fn parse<'a>(lines: impl Iterator<Item = &'a str>) -> Result<Vec<i64>, CalorieError> {
|
||||||
Ok(lines
|
Ok(lines
|
||||||
.iter()
|
|
||||||
.batching(|it| {
|
.batching(|it| {
|
||||||
let result = it
|
let result = it
|
||||||
.take_while(|line| !line.is_empty())
|
.take_while(|line| !line.is_empty())
|
||||||
|
|
@ -57,13 +60,13 @@ pub enum CalorieError {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(24_000);
|
let expected = ResultType::Integer(24_000);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -74,7 +77,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(45_000);
|
let expected = ResultType::Integer(45_000);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -84,9 +87,10 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse() -> Result<()> {
|
fn test_parse() -> Result<()> {
|
||||||
let lines = read_lines(DAY_NUMBER, "example01.txt")?;
|
let lines = read_string(DAY_NUMBER, "example01.txt")?;
|
||||||
|
let lines = split_lines(&lines);
|
||||||
let expected = vec![6000, 4000, 11000, 24000, 10000];
|
let expected = vec![6000, 4000, 11000, 24000, 10000];
|
||||||
let result = Day::parse(&lines)?;
|
let result = Day::parse(lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -12,9 +14,8 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let sum = lines
|
let sum = split_lines(lines)
|
||||||
.iter()
|
|
||||||
.map(|line| Rps::parse_line(line))
|
.map(|line| Rps::parse_line(line))
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -24,9 +25,8 @@ impl DayTrait for Day {
|
||||||
Ok(ResultType::Integer(sum))
|
Ok(ResultType::Integer(sum))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let sum = lines
|
let sum = split_lines(lines)
|
||||||
.iter()
|
|
||||||
.map(|line| Strategy::parse_line(line))
|
.map(|line| Strategy::parse_line(line))
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -155,7 +155,7 @@ impl TryFrom<&str> for Strategy {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -182,7 +182,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(15);
|
let expected = ResultType::Integer(15);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -214,7 +214,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(12);
|
let expected = ResultType::Integer(12);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -11,9 +13,8 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let sum = lines
|
let sum = split_lines(lines)
|
||||||
.iter()
|
|
||||||
.map(|line| find_double(line))
|
.map(|line| find_double(line))
|
||||||
.map_ok(priority)
|
.map_ok(priority)
|
||||||
.flatten_ok()
|
.flatten_ok()
|
||||||
|
|
@ -21,12 +22,11 @@ impl DayTrait for Day {
|
||||||
Ok(ResultType::Integer(sum))
|
Ok(ResultType::Integer(sum))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let sum = lines
|
let sum = split_lines(lines)
|
||||||
.iter()
|
|
||||||
.chunks(3)
|
.chunks(3)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|chunk| find_badge(&chunk.collect::<Vec<_>>()))
|
.map(|chunk| find_badge(&chunk.collect_vec()))
|
||||||
.map_ok(priority)
|
.map_ok(priority)
|
||||||
.flatten_ok()
|
.flatten_ok()
|
||||||
.fold_ok(0, |a, b| a + b)?;
|
.fold_ok(0, |a, b| a + b)?;
|
||||||
|
|
@ -71,7 +71,7 @@ fn find_double(content: &str) -> Result<char, RucksackError> {
|
||||||
Err(RucksackError::NoDoubleFound)?
|
Err(RucksackError::NoDoubleFound)?
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_badge(elves: &[&String]) -> Result<char, RucksackError> {
|
fn find_badge(elves: &[&str]) -> Result<char, RucksackError> {
|
||||||
if elves.len() < 2 {
|
if elves.len() < 2 {
|
||||||
return Err(RucksackError::NeedAtLeastTwo);
|
return Err(RucksackError::NeedAtLeastTwo);
|
||||||
};
|
};
|
||||||
|
|
@ -92,7 +92,7 @@ fn find_badge(elves: &[&String]) -> Result<char, RucksackError> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -108,7 +108,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(157);
|
let expected = ResultType::Integer(157);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -119,7 +119,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(70);
|
let expected = ResultType::Integer(70);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
@ -12,18 +14,16 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let sum = lines
|
let sum = split_lines(lines)
|
||||||
.iter()
|
|
||||||
.map(|line| parse(line))
|
.map(|line| parse(line))
|
||||||
.filter_ok(fully_contained)
|
.filter_ok(fully_contained)
|
||||||
.fold_ok(0i64, |a, _| a + 1)?;
|
.fold_ok(0i64, |a, _| a + 1)?;
|
||||||
Ok(ResultType::Integer(sum))
|
Ok(ResultType::Integer(sum))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let sum = lines
|
let sum = split_lines(lines)
|
||||||
.iter()
|
|
||||||
.map(|line| parse(line))
|
.map(|line| parse(line))
|
||||||
.filter_ok(overlaps)
|
.filter_ok(overlaps)
|
||||||
.fold_ok(0i64, |a, _| a + 1)?;
|
.fold_ok(0i64, |a, _| a + 1)?;
|
||||||
|
|
@ -77,7 +77,7 @@ fn overlaps((first, second): &(Range, Range)) -> bool {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -93,7 +93,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(2);
|
let expected = ResultType::Integer(2);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -104,7 +104,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(4);
|
let expected = ResultType::Integer(4);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
|
use itertools::Itertools;
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -11,7 +14,7 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let (mut stacks, commands) = parse(lines)?;
|
let (mut stacks, commands) = parse(lines)?;
|
||||||
for command in commands {
|
for command in commands {
|
||||||
command.with_turn(&mut stacks)?;
|
command.with_turn(&mut stacks)?;
|
||||||
|
|
@ -19,7 +22,7 @@ impl DayTrait for Day {
|
||||||
Ok(ResultType::String(get_word(&stacks)))
|
Ok(ResultType::String(get_word(&stacks)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let (mut stacks, commands) = parse(lines)?;
|
let (mut stacks, commands) = parse(lines)?;
|
||||||
for command in commands {
|
for command in commands {
|
||||||
command.without_turn(&mut stacks)?;
|
command.without_turn(&mut stacks)?;
|
||||||
|
|
@ -57,11 +60,13 @@ fn parse_crate(line: &str) -> Vec<Option<char>> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_all_crates(line_it: &mut dyn Iterator<Item = &String>) -> Result<Vec<String>, CrateError> {
|
fn parse_all_crates<'a>(
|
||||||
let rows: Vec<_> = line_it
|
line_it: &mut dyn Iterator<Item = &'a str>,
|
||||||
|
) -> Result<Vec<String>, CrateError> {
|
||||||
|
let rows = line_it
|
||||||
.take_while(|line| !line.is_empty())
|
.take_while(|line| !line.is_empty())
|
||||||
.map(|row| parse_crate(row))
|
.map(|row| parse_crate(row))
|
||||||
.collect();
|
.collect_vec();
|
||||||
let Some(stacks_count) = rows.iter().map(|row| row.len()).max() else {
|
let Some(stacks_count) = rows.iter().map(|row| row.len()).max() else {
|
||||||
return Err(CrateError::NoCargoStacksGiven);
|
return Err(CrateError::NoCargoStacksGiven);
|
||||||
};
|
};
|
||||||
|
|
@ -79,11 +84,11 @@ fn parse_all_crates(line_it: &mut dyn Iterator<Item = &String>) -> Result<Vec<St
|
||||||
Ok(stacks)
|
Ok(stacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(line: &[String]) -> Result<(Vec<String>, Vec<Move>), CrateError> {
|
fn parse<'a>(lines: &'a str) -> Result<(Vec<String>, Vec<Move>), CrateError> {
|
||||||
let mut iter = line.iter();
|
let mut iter = split_lines(lines);
|
||||||
let stacks = parse_all_crates(&mut iter)?;
|
let stacks = parse_all_crates(&mut iter)?;
|
||||||
let moves: Vec<_> = iter
|
let moves: Vec<_> = iter
|
||||||
.map(|command| Move::try_from(command.as_str()))
|
.map(|command| Move::try_from(command))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
Ok((stacks, moves))
|
Ok((stacks, moves))
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +172,7 @@ impl TryFrom<&str> for Move {
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -191,14 +196,15 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_all_crates() -> Result<()> {
|
fn test_parse_all_crates() -> Result<()> {
|
||||||
let lines = vec![
|
let lines = vec![
|
||||||
" [D] ".to_owned(),
|
" [D] ",
|
||||||
"[N] [C] ".to_owned(),
|
"[N] [C] ",
|
||||||
"[Z] [M] [P]".to_owned(),
|
"[Z] [M] [P]",
|
||||||
" 1 2 3 ".to_owned(),
|
" 1 2 3 ",
|
||||||
"".to_owned(),
|
"",
|
||||||
"move 1 from to 2 1".to_owned(),
|
"move 1 from to 2 1",
|
||||||
];
|
]
|
||||||
let mut input = lines.iter();
|
.join("\n");
|
||||||
|
let mut input = split_lines(&lines);
|
||||||
let expected = vec!["NZ1".to_owned(), "DCM2".to_owned(), "P3".to_owned()];
|
let expected = vec!["NZ1".to_owned(), "DCM2".to_owned(), "P3".to_owned()];
|
||||||
let result = parse_all_crates(&mut input)?;
|
let result = parse_all_crates(&mut input)?;
|
||||||
|
|
||||||
|
|
@ -221,7 +227,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::String("CMZ".to_owned());
|
let expected = ResultType::String("CMZ".to_owned());
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -243,7 +249,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::String("MCD".to_owned());
|
let expected = ResultType::String("MCD".to_owned());
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let count = find_marker(&lines[0], 4)?;
|
let count = find_marker(lines.trim_end(), 4)?;
|
||||||
Ok(ResultType::Integer(count as i64))
|
Ok(ResultType::Integer(count as i64))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let count = find_marker(&lines[0], 14)?;
|
let count = find_marker(lines.trim_end(), 14)?;
|
||||||
Ok(ResultType::Integer(count as i64))
|
Ok(ResultType::Integer(count as i64))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +82,7 @@ fn find_marker(word: &str, marker_length: usize) -> Result<usize, MarkerError> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -98,7 +98,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(10);
|
let expected = ResultType::Integer(10);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -109,7 +109,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(29);
|
let expected = ResultType::Integer(29);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ use std::{
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -16,7 +18,8 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
|
let lines = split_lines(lines);
|
||||||
let root = Directory::parse(lines)?;
|
let root = Directory::parse(lines)?;
|
||||||
let result = root
|
let result = root
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -32,9 +35,10 @@ impl DayTrait for Day {
|
||||||
Ok(ResultType::Integer(result))
|
Ok(ResultType::Integer(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
const AVAIL: i64 = 70_000_000;
|
const AVAIL: i64 = 70_000_000;
|
||||||
const REQUIRED: i64 = 30_000_000;
|
const REQUIRED: i64 = 30_000_000;
|
||||||
|
let lines = split_lines(lines);
|
||||||
let root = Directory::parse(lines)?;
|
let root = Directory::parse(lines)?;
|
||||||
let to_free = REQUIRED - (AVAIL - root.size());
|
let to_free = REQUIRED - (AVAIL - root.size());
|
||||||
let result = root
|
let result = root
|
||||||
|
|
@ -167,7 +171,7 @@ impl Directory {
|
||||||
sub_node.map(|node| Directory::create(node.clone()))
|
sub_node.map(|node| Directory::create(node.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(lines: &[String]) -> Result<Directory, DirectoryError> {
|
pub fn parse<'a>(lines: impl Iterator<Item = &'a str>) -> Result<Directory, DirectoryError> {
|
||||||
let root = Directory::root();
|
let root = Directory::root();
|
||||||
let mut current = root.clone();
|
let mut current = root.clone();
|
||||||
for line in lines {
|
for line in lines {
|
||||||
|
|
@ -269,7 +273,7 @@ impl Iterator for DirectoryIterator {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -298,8 +302,9 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_total_size() -> Result<()> {
|
fn test_total_size() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let dir = Directory::parse(&lines)?;
|
let lines = split_lines(&lines);
|
||||||
|
let dir = Directory::parse(lines)?;
|
||||||
assert_eq!(dir.size(), 48381165);
|
assert_eq!(dir.size(), 48381165);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -308,9 +313,10 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iterator() -> Result<()> {
|
fn test_iterator() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let lines = split_lines(&lines);
|
||||||
let expected = vec!["/", "a", "e", "d"];
|
let expected = vec!["/", "a", "e", "d"];
|
||||||
let dir = Directory::parse(&lines)?;
|
let dir = Directory::parse(lines)?;
|
||||||
let result: Vec<_> = dir.iter().map(|dir| dir.name()).collect();
|
let result: Vec<_> = dir.iter().map(|dir| dir.name()).collect();
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
|
@ -320,7 +326,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(95437);
|
let expected = ResultType::Integer(95437);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -331,7 +337,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(24933642);
|
let expected = ResultType::Integer(24933642);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use itertools::{iproduct, FoldWhile, Itertools};
|
use itertools::{iproduct, FoldWhile, Itertools};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -11,14 +13,14 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let forest = Forest::parse(lines)?;
|
let forest = Forest::try_from(lines)?;
|
||||||
let result = forest.count_visible();
|
let result = forest.count_visible();
|
||||||
Ok(ResultType::Integer(result))
|
Ok(ResultType::Integer(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let forest = Forest::parse(lines)?;
|
let forest = Forest::try_from(lines)?;
|
||||||
let result = forest.best_score();
|
let result = forest.best_score();
|
||||||
Ok(ResultType::Integer(result))
|
Ok(ResultType::Integer(result))
|
||||||
}
|
}
|
||||||
|
|
@ -67,18 +69,6 @@ impl Forest {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(lines: &[String]) -> Result<Forest, ForestError> {
|
|
||||||
lines
|
|
||||||
.iter()
|
|
||||||
.map(|line| {
|
|
||||||
line.chars()
|
|
||||||
.map(|height| height.to_digit(10).ok_or(ForestError::NoLegalDigit(height)))
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.and_then(Forest::create)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn count_visible(&self) -> i64 {
|
pub fn count_visible(&self) -> i64 {
|
||||||
let mut vis_count = 2 * (self.width + self.height - 2) as i64;
|
let mut vis_count = 2 * (self.width + self.height - 2) as i64;
|
||||||
let mut visible = vec![vec![false; self.width - 2]; self.height - 2];
|
let mut visible = vec![vec![false; self.width - 2]; self.height - 2];
|
||||||
|
|
@ -191,17 +181,32 @@ impl Forest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Forest {
|
||||||
|
type Error = ForestError;
|
||||||
|
|
||||||
|
fn try_from(lines: &str) -> Result<Self, Self::Error> {
|
||||||
|
split_lines(lines)
|
||||||
|
.map(|line| {
|
||||||
|
line.chars()
|
||||||
|
.map(|height| height.to_digit(10).ok_or(ForestError::NoLegalDigit(height)))
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.and_then(Forest::create)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse() -> Result<()> {
|
fn test_parse() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let forest = Forest::parse(&lines)?;
|
let forest = Forest::try_from(lines.as_str())?;
|
||||||
assert_eq!(forest.width, 5);
|
assert_eq!(forest.width, 5);
|
||||||
assert_eq!(forest.height, 5);
|
assert_eq!(forest.height, 5);
|
||||||
|
|
||||||
|
|
@ -211,7 +216,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(21);
|
let expected = ResultType::Integer(21);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -222,7 +227,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(8);
|
let expected = ResultType::Integer(8);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use crate::common::{direction::Direction, pos::Pos};
|
use crate::common::{direction::Direction, file::split_lines, pos::Pos};
|
||||||
|
use itertools::Itertools;
|
||||||
use std::{collections::HashSet, num::ParseIntError};
|
use std::{collections::HashSet, num::ParseIntError};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -12,14 +13,14 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let commands = parse_commands(lines)?;
|
let commands = parse_commands(lines)?;
|
||||||
let mut rope = Rope::create(2);
|
let mut rope = Rope::create(2);
|
||||||
let result = rope.run(&commands);
|
let result = rope.run(&commands);
|
||||||
Ok(ResultType::Integer(result))
|
Ok(ResultType::Integer(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let commands = parse_commands(lines)?;
|
let commands = parse_commands(lines)?;
|
||||||
let mut rope = Rope::create(10);
|
let mut rope = Rope::create(10);
|
||||||
let result = rope.run(&commands);
|
let result = rope.run(&commands);
|
||||||
|
|
@ -39,12 +40,12 @@ enum RopeError {
|
||||||
StepParseError(#[from] ParseIntError),
|
StepParseError(#[from] ParseIntError),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_commands(lines: &[String]) -> Result<Vec<(Direction, usize)>, RopeError> {
|
fn parse_commands(lines: &str) -> Result<Vec<(Direction, usize)>, RopeError> {
|
||||||
lines.iter().map(parse_line).collect::<Result<_, _>>()
|
split_lines(lines).map(parse_line).try_collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_line(line: &String) -> Result<(Direction, usize), RopeError> {
|
fn parse_line(line: &str) -> Result<(Direction, usize), RopeError> {
|
||||||
match line.split_whitespace().collect::<Vec<_>>()[..] {
|
match line.split_whitespace().collect_vec()[..] {
|
||||||
[direction, steps] => {
|
[direction, steps] => {
|
||||||
let direction = match direction {
|
let direction = match direction {
|
||||||
"U" => Direction::North,
|
"U" => Direction::North,
|
||||||
|
|
@ -125,13 +126,13 @@ impl Rope {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(13);
|
let expected = ResultType::Integer(13);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -142,7 +143,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example02.txt")?;
|
let lines = read_string(day.get_day_number(), "example02.txt")?;
|
||||||
let expected = ResultType::Integer(36);
|
let expected = ResultType::Integer(36);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use std::{num::ParseIntError, slice::Iter};
|
use std::{num::ParseIntError, slice::Iter};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -11,18 +13,16 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let instructions = lines
|
let instructions = split_lines(lines)
|
||||||
.iter()
|
|
||||||
.map(|line| Instruction::parse(line))
|
.map(|line| Instruction::parse(line))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let result = CpuCycles::signal_strength(&instructions, &[20, 60, 100, 140, 180, 220]);
|
let result = CpuCycles::signal_strength(&instructions, &[20, 60, 100, 140, 180, 220]);
|
||||||
Ok(ResultType::Integer(result as i64))
|
Ok(ResultType::Integer(result as i64))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let instructions = lines
|
let instructions = split_lines(lines)
|
||||||
.iter()
|
|
||||||
.map(|line| Instruction::parse(line))
|
.map(|line| Instruction::parse(line))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let result = CpuCycles::draw(&instructions);
|
let result = CpuCycles::draw(&instructions);
|
||||||
|
|
@ -128,8 +128,9 @@ impl<'a> Iterator for CpuCycles<'a> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() -> Result<()> {
|
fn test_simple() -> Result<()> {
|
||||||
|
|
@ -144,7 +145,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(13140);
|
let expected = ResultType::Integer(13140);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -155,8 +156,12 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Lines(read_lines(day.get_day_number(), "expected01.txt")?);
|
let expected_lines = read_string(day.get_day_number(), "expected01.txt")?;
|
||||||
|
let expected_lines = split_lines(&expected_lines)
|
||||||
|
.map(|s| s.to_owned())
|
||||||
|
.collect_vec();
|
||||||
|
let expected = ResultType::Lines(expected_lines);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
@ -13,13 +15,13 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let troop = Troop::parse(lines)?;
|
let troop = Troop::try_from(lines)?;
|
||||||
Ok(ResultType::Integer(troop.play(20)))
|
Ok(ResultType::Integer(troop.play(20)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let troop = Troop::parse(lines)?;
|
let troop = Troop::try_from(lines)?;
|
||||||
Ok(ResultType::Integer(troop.play_again(10_000)))
|
Ok(ResultType::Integer(troop.play_again(10_000)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +92,7 @@ impl Monkey {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_one(
|
pub fn parse_one(
|
||||||
iterator: &mut dyn Iterator<Item = &String>,
|
iterator: &mut dyn Iterator<Item = &str>,
|
||||||
) -> Result<Option<Monkey>, MonkeyError> {
|
) -> Result<Option<Monkey>, MonkeyError> {
|
||||||
if let Some(line) = iterator.next() {
|
if let Some(line) = iterator.next() {
|
||||||
let number: usize = Monkey::parse_line(&MONKEY, line)?.parse()?;
|
let number: usize = Monkey::parse_line(&MONKEY, line)?.parse()?;
|
||||||
|
|
@ -142,16 +144,20 @@ struct Troop {
|
||||||
monkeys: Vec<Monkey>,
|
monkeys: Vec<Monkey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Troop {
|
impl TryFrom<&str> for Troop {
|
||||||
pub fn parse(lines: &[String]) -> Result<Troop, MonkeyError> {
|
type Error = MonkeyError;
|
||||||
let mut iter = lines.iter();
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
let mut iter = split_lines(value);
|
||||||
let mut monkeys = Vec::new();
|
let mut monkeys = Vec::new();
|
||||||
while let Some(monkey) = Monkey::parse_one(&mut iter)? {
|
while let Some(monkey) = Monkey::parse_one(&mut iter)? {
|
||||||
monkeys.push(monkey);
|
monkeys.push(monkey);
|
||||||
}
|
}
|
||||||
Ok(Troop { monkeys })
|
Ok(Troop { monkeys })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Troop {
|
||||||
fn do_play<F>(&self, rounds: usize, alter: F) -> i64
|
fn do_play<F>(&self, rounds: usize, alter: F) -> i64
|
||||||
where
|
where
|
||||||
F: Fn(i64) -> i64,
|
F: Fn(i64) -> i64,
|
||||||
|
|
@ -191,13 +197,14 @@ impl Troop {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse() -> Result<()> {
|
fn test_parse() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
|
let mut lines = split_lines(&lines);
|
||||||
let expected = Monkey {
|
let expected = Monkey {
|
||||||
number: 0,
|
number: 0,
|
||||||
items: vec![79, 98],
|
items: vec![79, 98],
|
||||||
|
|
@ -206,7 +213,7 @@ mod test {
|
||||||
good_monkey: 2,
|
good_monkey: 2,
|
||||||
bad_monkey: 3,
|
bad_monkey: 3,
|
||||||
};
|
};
|
||||||
let result = Monkey::parse_one(&mut lines.iter())?.unwrap();
|
let result = Monkey::parse_one(&mut lines)?.unwrap();
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -215,9 +222,9 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_all() -> Result<()> {
|
fn test_parse_all() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = 4;
|
let expected = 4;
|
||||||
let result = Troop::parse(&lines)?;
|
let result = Troop::try_from(lines.as_str())?;
|
||||||
assert_eq!(result.monkeys.len(), expected);
|
assert_eq!(result.monkeys.len(), expected);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -226,7 +233,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(10605);
|
let expected = ResultType::Integer(10605);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -237,7 +244,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(2713310158);
|
let expected = ResultType::Integer(2713310158);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::{BinaryHeap, HashSet};
|
use std::collections::{BinaryHeap, HashSet};
|
||||||
|
|
||||||
use crate::common::pos::Pos;
|
use crate::common::{file::split_lines, pos::Pos};
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -14,13 +14,13 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let valley = Valley::parse(lines)?;
|
let valley = Valley::try_from(lines)?;
|
||||||
Ok(ResultType::Integer(valley.walk()? as i64))
|
Ok(ResultType::Integer(valley.walk()? as i64))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let valley = Valley::parse(lines)?;
|
let valley = Valley::try_from(lines)?;
|
||||||
Ok(ResultType::Integer(valley.walk_short()? as i64))
|
Ok(ResultType::Integer(valley.walk_short()? as i64))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -158,13 +158,16 @@ struct Valley {
|
||||||
width: usize,
|
width: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Valley {
|
impl TryFrom<&str> for Valley {
|
||||||
pub fn parse(lines: &[String]) -> Result<Valley, ValleyError> {
|
type Error = ValleyError;
|
||||||
|
|
||||||
|
fn try_from(lines: &str) -> Result<Self, Self::Error> {
|
||||||
|
let lines = split_lines(lines);
|
||||||
let mut map = Vec::new();
|
let mut map = Vec::new();
|
||||||
let mut start = None;
|
let mut start = None;
|
||||||
let mut exit = None;
|
let mut exit = None;
|
||||||
let mut valley_width = None;
|
let mut valley_width = None;
|
||||||
for (y, row) in lines.iter().enumerate() {
|
for (y, row) in lines.enumerate() {
|
||||||
let mut height_row = Vec::new();
|
let mut height_row = Vec::new();
|
||||||
for (x, height_char) in row.chars().enumerate() {
|
for (x, height_char) in row.chars().enumerate() {
|
||||||
match height_char {
|
match height_char {
|
||||||
|
|
@ -205,7 +208,9 @@ impl Valley {
|
||||||
width,
|
width,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Valley {
|
||||||
fn get_height(&self, pos: Pos<usize>) -> char {
|
fn get_height(&self, pos: Pos<usize>) -> char {
|
||||||
self.map[pos.y()][pos.x()]
|
self.map[pos.y()][pos.x()]
|
||||||
}
|
}
|
||||||
|
|
@ -255,14 +260,14 @@ impl Valley {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse() -> Result<()> {
|
fn test_parse() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let valley = Valley::parse(&lines)?;
|
let valley = Valley::try_from(lines.as_str())?;
|
||||||
assert_eq!(valley.width, 8);
|
assert_eq!(valley.width, 8);
|
||||||
assert_eq!(valley.start, Pos::new(0, 0));
|
assert_eq!(valley.start, Pos::new(0, 0));
|
||||||
assert_eq!(valley.exit, Pos::new(5, 2));
|
assert_eq!(valley.exit, Pos::new(5, 2));
|
||||||
|
|
@ -273,7 +278,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(31);
|
let expected = ResultType::Integer(31);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -284,7 +289,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(29);
|
let expected = ResultType::Integer(29);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -13,7 +15,7 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let packets = Packets::parse_all(lines)?;
|
let packets = Packets::parse_all(lines)?;
|
||||||
let result: i64 = packets
|
let result: i64 = packets
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -30,7 +32,7 @@ impl DayTrait for Day {
|
||||||
Ok(ResultType::Integer(result))
|
Ok(ResultType::Integer(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let small = Packets::parse("[[2]]")?;
|
let small = Packets::parse("[[2]]")?;
|
||||||
let large = Packets::parse("[[6]]")?;
|
let large = Packets::parse("[[6]]")?;
|
||||||
let mut pos_small = 1;
|
let mut pos_small = 1;
|
||||||
|
|
@ -144,9 +146,8 @@ impl Packets {
|
||||||
Err(PacketError::PrematureEndOfInput)
|
Err(PacketError::PrematureEndOfInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_all(lines: &[String]) -> Result<Vec<Packets>, PacketError> {
|
pub fn parse_all(lines: &str) -> Result<Vec<Packets>, PacketError> {
|
||||||
lines
|
split_lines(lines)
|
||||||
.iter()
|
|
||||||
.filter(|line| !line.is_empty())
|
.filter(|line| !line.is_empty())
|
||||||
.map(|line| Packets::parse(line))
|
.map(|line| Packets::parse(line))
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
|
@ -156,7 +157,7 @@ impl Packets {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -174,7 +175,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_all() -> Result<()> {
|
fn test_parse_all() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let result = Packets::parse_all(&lines)?;
|
let result = Packets::parse_all(&lines)?;
|
||||||
assert_eq!(result.len(), 16);
|
assert_eq!(result.len(), 16);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -183,7 +184,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_compare_all() -> Result<()> {
|
fn test_compare_all() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = vec![true, true, false, true, false, true, false, false];
|
let expected = vec![true, true, false, true, false, true, false, false];
|
||||||
let result = Packets::parse_all(&lines)?;
|
let result = Packets::parse_all(&lines)?;
|
||||||
let compare = result
|
let compare = result
|
||||||
|
|
@ -199,7 +200,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(13);
|
let expected = ResultType::Integer(13);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -210,7 +211,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(140);
|
let expected = ResultType::Integer(140);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||||
use std::{collections::HashSet, num::ParseIntError};
|
use std::{collections::HashSet, num::ParseIntError};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::common::pos::Pos;
|
use crate::common::{file::split_lines, pos::Pos};
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
|
|
||||||
|
|
@ -15,13 +15,13 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let cave = Cave::parse(lines)?;
|
let cave = Cave::try_from(lines)?;
|
||||||
Ok(ResultType::Integer(cave.drop_bottomless() as i64))
|
Ok(ResultType::Integer(cave.drop_bottomless() as i64))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let cave = Cave::parse(lines)?;
|
let cave = Cave::try_from(lines)?;
|
||||||
Ok(ResultType::Integer(cave.drop_floor()))
|
Ok(ResultType::Integer(cave.drop_floor()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -51,6 +51,46 @@ struct Cave {
|
||||||
height: i32,
|
height: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Cave {
|
||||||
|
type Error = CaveError;
|
||||||
|
|
||||||
|
fn try_from(lines: &str) -> Result<Self, Self::Error> {
|
||||||
|
let lines = split_lines(lines);
|
||||||
|
let mut cave: HashSet<Pos<i32>> = HashSet::new();
|
||||||
|
for line in lines {
|
||||||
|
cave.extend(Cave::parse_one(line)?.iter());
|
||||||
|
}
|
||||||
|
if cave.is_empty() {
|
||||||
|
Err(CaveError::EmptyCave)
|
||||||
|
} else {
|
||||||
|
let mut max_x = i32::MIN;
|
||||||
|
let mut min_x = i32::MAX;
|
||||||
|
let mut max_y = i32::MIN;
|
||||||
|
for block in &cave {
|
||||||
|
if block.x() < min_x {
|
||||||
|
min_x = block.x();
|
||||||
|
}
|
||||||
|
if block.x() > max_x {
|
||||||
|
max_x = block.x();
|
||||||
|
}
|
||||||
|
if block.y() > max_y {
|
||||||
|
max_y = block.y()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut map = vec![vec![false; (max_x - min_x + 1) as usize]; (max_y + 1) as usize];
|
||||||
|
for block in cave {
|
||||||
|
map[block.y() as usize][(block.x() - min_x) as usize] = true;
|
||||||
|
}
|
||||||
|
Ok(Cave {
|
||||||
|
map,
|
||||||
|
min_x,
|
||||||
|
max_x,
|
||||||
|
height: max_y + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Cave {
|
impl Cave {
|
||||||
fn get(&self, map: &[Vec<bool>], pos: Pos<i32>) -> bool {
|
fn get(&self, map: &[Vec<bool>], pos: Pos<i32>) -> bool {
|
||||||
if pos.y() >= self.height || pos.x() < self.min_x || pos.x() > self.max_x {
|
if pos.y() >= self.height || pos.x() < self.min_x || pos.x() > self.max_x {
|
||||||
|
|
@ -118,41 +158,6 @@ impl Cave {
|
||||||
Ok(blocks)
|
Ok(blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(lines: &[String]) -> Result<Cave, CaveError> {
|
|
||||||
let mut cave: HashSet<Pos<i32>> = HashSet::new();
|
|
||||||
for line in lines {
|
|
||||||
cave.extend(Cave::parse_one(line)?.iter());
|
|
||||||
}
|
|
||||||
if cave.is_empty() {
|
|
||||||
Err(CaveError::EmptyCave)
|
|
||||||
} else {
|
|
||||||
let mut max_x = i32::MIN;
|
|
||||||
let mut min_x = i32::MAX;
|
|
||||||
let mut max_y = i32::MIN;
|
|
||||||
for block in &cave {
|
|
||||||
if block.x() < min_x {
|
|
||||||
min_x = block.x();
|
|
||||||
}
|
|
||||||
if block.x() > max_x {
|
|
||||||
max_x = block.x();
|
|
||||||
}
|
|
||||||
if block.y() > max_y {
|
|
||||||
max_y = block.y()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut map = vec![vec![false; (max_x - min_x + 1) as usize]; (max_y + 1) as usize];
|
|
||||||
for block in cave {
|
|
||||||
map[block.y() as usize][(block.x() - min_x) as usize] = true;
|
|
||||||
}
|
|
||||||
Ok(Cave {
|
|
||||||
map,
|
|
||||||
min_x,
|
|
||||||
max_x,
|
|
||||||
height: max_y + 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drop_bottomless(&self) -> u32 {
|
pub fn drop_bottomless(&self) -> u32 {
|
||||||
let mut drops = 0;
|
let mut drops = 0;
|
||||||
let mut filled_map = self.map.to_vec();
|
let mut filled_map = self.map.to_vec();
|
||||||
|
|
@ -243,7 +248,7 @@ impl Cave {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{common::file::read_lines, hashset};
|
use crate::{common::file::read_string, hashset};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -259,8 +264,8 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_all() -> Result<()> {
|
fn test_parse_all() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let result = Cave::parse(&lines)?;
|
let result = Cave::try_from(lines.as_str())?;
|
||||||
assert_eq!(result.min_x, 494);
|
assert_eq!(result.min_x, 494);
|
||||||
assert_eq!(result.max_x, 503);
|
assert_eq!(result.max_x, 503);
|
||||||
assert_eq!(result.height, 10);
|
assert_eq!(result.height, 10);
|
||||||
|
|
@ -271,7 +276,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(24);
|
let expected = ResultType::Integer(24);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -282,7 +287,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(93);
|
let expected = ResultType::Integer(93);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use crate::common::pos::Pos;
|
use crate::common::{file::split_lines, pos::Pos};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
@ -15,14 +15,22 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
|
let lines = split_lines(lines).collect_vec();
|
||||||
|
if lines.len() < 2 {
|
||||||
|
Err(SensorError::ToFewLines)?;
|
||||||
|
}
|
||||||
let row = lines[0].parse()?;
|
let row = lines[0].parse()?;
|
||||||
let (sensors, beacons) = Day::parse_all(&lines[1..])?;
|
let (sensors, beacons) = Day::parse_all(&lines[1..])?;
|
||||||
let result = Day::count_coverage_at(&sensors, &beacons, row);
|
let result = Day::count_coverage_at(&sensors, &beacons, row);
|
||||||
Ok(ResultType::Integer(result))
|
Ok(ResultType::Integer(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
|
let lines = split_lines(lines).collect_vec();
|
||||||
|
if lines.len() < 2 {
|
||||||
|
Err(SensorError::ToFewLines)?;
|
||||||
|
}
|
||||||
let (sensors, _) = Day::parse_all(&lines[1..])?;
|
let (sensors, _) = Day::parse_all(&lines[1..])?;
|
||||||
let mid_lines = Day::dividing_lines(&sensors);
|
let mid_lines = Day::dividing_lines(&sensors);
|
||||||
let points = mid_lines
|
let points = mid_lines
|
||||||
|
|
@ -43,7 +51,7 @@ impl DayTrait for Day {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Day {
|
impl Day {
|
||||||
fn parse_all(lines: &[String]) -> Result<(HashSet<Sensor>, HashSet<Pos<i64>>), SensorError> {
|
fn parse_all(lines: &[&str]) -> Result<(HashSet<Sensor>, HashSet<Pos<i64>>), SensorError> {
|
||||||
let mut sensors = HashSet::new();
|
let mut sensors = HashSet::new();
|
||||||
let mut beacons = HashSet::new();
|
let mut beacons = HashSet::new();
|
||||||
for line in lines {
|
for line in lines {
|
||||||
|
|
@ -91,6 +99,9 @@ enum SensorError {
|
||||||
|
|
||||||
#[error("Did not find exactly one point for the sensor: {0}")]
|
#[error("Did not find exactly one point for the sensor: {0}")]
|
||||||
NotExactlyOneResult(usize),
|
NotExactlyOneResult(usize),
|
||||||
|
|
||||||
|
#[error("Too few lines in input.txt")]
|
||||||
|
ToFewLines,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -256,7 +267,7 @@ impl Sensor {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse() -> Result<()> {
|
fn test_parse() -> Result<()> {
|
||||||
|
|
@ -295,7 +306,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(26);
|
let expected = ResultType::Integer(26);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -306,7 +317,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(56000011);
|
let expected = ResultType::Integer(56000011);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ use std::iter::Sum;
|
||||||
use std::ops::{Add, Deref, Mul, Sub};
|
use std::ops::{Add, Deref, Mul, Sub};
|
||||||
use std::{collections::BinaryHeap, num::ParseIntError};
|
use std::{collections::BinaryHeap, num::ParseIntError};
|
||||||
|
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use const_format::concatcp;
|
use const_format::concatcp;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
@ -21,13 +23,13 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let system = ValveSystem::build(lines, "AA")?;
|
let system = ValveSystem::build(lines, "AA")?;
|
||||||
let result = system.maximum_flow(system.single_actor(Time(30)))?;
|
let result = system.maximum_flow(system.single_actor(Time(30)))?;
|
||||||
Ok(ResultType::Integer(*result))
|
Ok(ResultType::Integer(*result))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let system = ValveSystem::build(lines, "AA")?;
|
let system = ValveSystem::build(lines, "AA")?;
|
||||||
let result = system.maximum_flow(system.double_actor(Time(26)))?;
|
let result = system.maximum_flow(system.double_actor(Time(26)))?;
|
||||||
Ok(ResultType::Integer(*result))
|
Ok(ResultType::Integer(*result))
|
||||||
|
|
@ -264,10 +266,9 @@ struct ValveSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValveSystem {
|
impl ValveSystem {
|
||||||
fn build(lines: &[String], start: &str) -> Result<ValveSystem, ValveError> {
|
fn build(lines: &str, start: &str) -> Result<ValveSystem, ValveError> {
|
||||||
let mut raw = lines
|
let mut raw = split_lines(lines)
|
||||||
.iter()
|
.map(|line| RawValve::try_from(line))
|
||||||
.map(|line| RawValve::try_from(line.as_str()))
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
raw.sort_unstable_by_key(|valve| valve.id);
|
raw.sort_unstable_by_key(|valve| valve.id);
|
||||||
|
|
||||||
|
|
@ -912,13 +913,13 @@ impl Hash for HashableState {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(1651);
|
let expected = ResultType::Integer(1651);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -949,7 +950,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_system() -> Result<()> {
|
fn parse_system() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let system = ValveSystem::build(&lines, "AA")?;
|
let system = ValveSystem::build(&lines, "AA")?;
|
||||||
|
|
||||||
assert_eq!(10, system.len());
|
assert_eq!(10, system.len());
|
||||||
|
|
@ -966,7 +967,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn single_state_init() -> Result<()> {
|
fn single_state_init() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let system = ValveSystem::build(&lines, "AA")?;
|
let system = ValveSystem::build(&lines, "AA")?;
|
||||||
let actor = system.single_actor(Time(5));
|
let actor = system.single_actor(Time(5));
|
||||||
|
|
||||||
|
|
@ -980,7 +981,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn next_single_actor_states() -> Result<()> {
|
fn next_single_actor_states() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let system = ValveSystem::build(&lines, "AA")?;
|
let system = ValveSystem::build(&lines, "AA")?;
|
||||||
let actor = system.single_actor(Time(5));
|
let actor = system.single_actor(Time(5));
|
||||||
let state = ValveState::init(&system, actor);
|
let state = ValveState::init(&system, actor);
|
||||||
|
|
@ -1002,7 +1003,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn next_single_states() -> Result<()> {
|
fn next_single_states() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let system = ValveSystem::build(&lines, "AA")?;
|
let system = ValveSystem::build(&lines, "AA")?;
|
||||||
let actor = system.single_actor(Time(6));
|
let actor = system.single_actor(Time(6));
|
||||||
let state = ValveState::init(&system, actor);
|
let state = ValveState::init(&system, actor);
|
||||||
|
|
@ -1023,7 +1024,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn double_state_init() -> Result<()> {
|
fn double_state_init() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let system = ValveSystem::build(&lines, "AA")?;
|
let system = ValveSystem::build(&lines, "AA")?;
|
||||||
let actor = system.double_actor(Time(5));
|
let actor = system.double_actor(Time(5));
|
||||||
let state = ValveState::init(&system, actor);
|
let state = ValveState::init(&system, actor);
|
||||||
|
|
@ -1037,7 +1038,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn next_double_actor_states() -> Result<()> {
|
fn next_double_actor_states() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let system = ValveSystem::build(&lines, "AA")?;
|
let system = ValveSystem::build(&lines, "AA")?;
|
||||||
let actor = system.double_actor(Time(5));
|
let actor = system.double_actor(Time(5));
|
||||||
let state = ValveState::init(&system, actor);
|
let state = ValveState::init(&system, actor);
|
||||||
|
|
@ -1068,7 +1069,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn next_double_states() -> Result<()> {
|
fn next_double_states() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let system = ValveSystem::build(&lines, "AA")?;
|
let system = ValveSystem::build(&lines, "AA")?;
|
||||||
let actor = system.double_actor(Time(5));
|
let actor = system.double_actor(Time(5));
|
||||||
let state = ValveState::init(&system, actor);
|
let state = ValveState::init(&system, actor);
|
||||||
|
|
@ -1090,7 +1091,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn special_double_stats() -> Result<()> {
|
fn special_double_stats() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let system = ValveSystem::build(&lines, "AA")?;
|
let system = ValveSystem::build(&lines, "AA")?;
|
||||||
let actor = DoubleActor(DoubleWork::Simultaneous(Index(1), Index(7), Time(19)));
|
let actor = DoubleActor(DoubleWork::Simultaneous(Index(1), Index(7), Time(19)));
|
||||||
let state = ValveState {
|
let state = ValveState {
|
||||||
|
|
@ -1132,7 +1133,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn double_long_run() -> Result<()> {
|
fn double_long_run() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let system = ValveSystem::build(&lines, "AA")?;
|
let system = ValveSystem::build(&lines, "AA")?;
|
||||||
let actor = DoubleActor(DoubleWork::Sequential(Index(3), Time(5), Index(7), Time(2)));
|
let actor = DoubleActor(DoubleWork::Sequential(Index(3), Time(5), Index(7), Time(2)));
|
||||||
let state = ValveState {
|
let state = ValveState {
|
||||||
|
|
@ -1158,7 +1159,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(1707);
|
let expected = ResultType::Integer(1707);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{cell::Cell, marker::PhantomData, ops::Index};
|
use std::{cell::Cell, marker::PhantomData, ops::Index};
|
||||||
|
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::{read_string, split_lines};
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
@ -16,16 +16,28 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?);
|
let pushes = Dispenser::new(
|
||||||
|
lines
|
||||||
|
.trim_end()
|
||||||
|
.chars()
|
||||||
|
.map(|c| Push::parse(c))
|
||||||
|
.try_collect()?,
|
||||||
|
)?;
|
||||||
|
|
||||||
let result = Day::run(pushes, 2022)?;
|
let result = Day::run(pushes, 2022)?;
|
||||||
|
|
||||||
Ok(ResultType::Integer(result as i64))
|
Ok(ResultType::Integer(result as i64))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?);
|
let pushes = Dispenser::new(
|
||||||
|
lines
|
||||||
|
.trim_end()
|
||||||
|
.chars()
|
||||||
|
.map(|c| Push::parse(c))
|
||||||
|
.try_collect()?,
|
||||||
|
)?;
|
||||||
|
|
||||||
let result = Day::run(pushes, 1000000000000)?;
|
let result = Day::run(pushes, 1000000000000)?;
|
||||||
|
|
||||||
|
|
@ -35,8 +47,8 @@ impl DayTrait for Day {
|
||||||
|
|
||||||
impl Day {
|
impl Day {
|
||||||
fn run(push_cycle: Dispenser<Push>, max_cycles: i64) -> Result<i64, RockError> {
|
fn run(push_cycle: Dispenser<Push>, max_cycles: i64) -> Result<i64, RockError> {
|
||||||
let raw = read_lines(DAY_NUMBER, "blocks.txt")?;
|
let raw = read_string(DAY_NUMBER, "blocks.txt")?;
|
||||||
let rock_cycle = Dispenser::new(Rock::parse(&raw)?);
|
let rock_cycle = Dispenser::new(Rock::parse(&raw)?)?;
|
||||||
let mut cycle = 0;
|
let mut cycle = 0;
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
|
@ -114,6 +126,9 @@ enum RockError {
|
||||||
|
|
||||||
#[error("Unknown direction: {0}")]
|
#[error("Unknown direction: {0}")]
|
||||||
UnknownDirection(char),
|
UnknownDirection(char),
|
||||||
|
|
||||||
|
#[error("A Dispenser must never be empty")]
|
||||||
|
EmptyDispenserNotAllowed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
|
@ -158,9 +173,8 @@ impl Rock {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(lines: &[String]) -> Result<Vec<Rock>, RockError> {
|
pub fn parse(lines: &str) -> Result<Vec<Rock>, RockError> {
|
||||||
lines
|
split_lines(lines)
|
||||||
.iter()
|
|
||||||
.batching(|it| {
|
.batching(|it| {
|
||||||
let blocks = it
|
let blocks = it
|
||||||
.skip_while(|line| line.is_empty())
|
.skip_while(|line| line.is_empty())
|
||||||
|
|
@ -330,11 +344,15 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Dispenser<'a, T> {
|
impl<'a, T> Dispenser<'a, T> {
|
||||||
pub fn new(data: Vec<T>) -> Self {
|
pub fn new(data: Vec<T>) -> Result<Self, RockError> {
|
||||||
Dispenser {
|
if data.is_empty() {
|
||||||
data,
|
Err(RockError::EmptyDispenserNotAllowed)
|
||||||
current: Cell::new(0),
|
} else {
|
||||||
_marker: PhantomData,
|
Ok(Dispenser {
|
||||||
|
data,
|
||||||
|
current: Cell::new(0),
|
||||||
|
_marker: PhantomData,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -351,13 +369,13 @@ impl<'a, T> Dispenser<'a, T> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(3068);
|
let expected = ResultType::Integer(3068);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -368,7 +386,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(1514285714288);
|
let expected = ResultType::Integer(1514285714288);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -379,7 +397,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn read_blocks() -> Result<()> {
|
fn read_blocks() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let raw = read_lines(day.get_day_number(), "blocks.txt")?;
|
let raw = read_string(day.get_day_number(), "blocks.txt")?;
|
||||||
let rocks = Rock::parse(&raw)?;
|
let rocks = Rock::parse(&raw)?;
|
||||||
let expected = Rock::new(vec![vec![true]; 4])?;
|
let expected = Rock::new(vec![vec![true]; 4])?;
|
||||||
assert_eq!(rocks[3], expected);
|
assert_eq!(rocks[3], expected);
|
||||||
|
|
@ -396,10 +414,16 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn drop_one() -> Result<()> {
|
fn drop_one() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?);
|
let pushes = Dispenser::new(
|
||||||
let raw = read_lines(day.get_day_number(), "blocks.txt")?;
|
lines
|
||||||
let rocks = Dispenser::new(Rock::parse(&raw)?);
|
.trim_end()
|
||||||
|
.chars()
|
||||||
|
.map(|c| Push::parse(c))
|
||||||
|
.try_collect()?,
|
||||||
|
)?;
|
||||||
|
let raw = read_string(day.get_day_number(), "blocks.txt")?;
|
||||||
|
let rocks = Dispenser::new(Rock::parse(&raw)?)?;
|
||||||
|
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
stack.one_rock(rocks.next(), &pushes);
|
stack.one_rock(rocks.next(), &pushes);
|
||||||
|
|
@ -412,8 +436,14 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn drop_some() -> Result<()> {
|
fn drop_some() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?);
|
let pushes = Dispenser::new(
|
||||||
|
lines
|
||||||
|
.trim_end()
|
||||||
|
.chars()
|
||||||
|
.map(|c| Push::parse(c))
|
||||||
|
.try_collect()?,
|
||||||
|
)?;
|
||||||
|
|
||||||
let result = Day::run(pushes, 10)?;
|
let result = Day::run(pushes, 10)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ use itertools::Itertools;
|
||||||
use std::{collections::HashSet, num::ParseIntError};
|
use std::{collections::HashSet, num::ParseIntError};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::common::file::split_lines;
|
||||||
|
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
|
|
||||||
const DAY_NUMBER: usize = 18;
|
const DAY_NUMBER: usize = 18;
|
||||||
|
|
@ -13,13 +15,13 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let blob = Blob::parse(lines)?;
|
let blob = Blob::try_from(lines)?;
|
||||||
Ok(ResultType::Integer(blob.count_sides()))
|
Ok(ResultType::Integer(blob.count_sides()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
let blob = Blob::parse(lines)?;
|
let blob = Blob::try_from(lines)?;
|
||||||
Ok(ResultType::Integer(blob.count_outside()))
|
Ok(ResultType::Integer(blob.count_outside()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,15 +133,18 @@ struct Blob {
|
||||||
droplets: HashSet<Droplet>,
|
droplets: HashSet<Droplet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Blob {
|
impl TryFrom<&str> for Blob {
|
||||||
fn parse(lines: &[String]) -> Result<Self, DropletError> {
|
type Error = DropletError;
|
||||||
let droplets = lines
|
|
||||||
.iter()
|
fn try_from(lines: &str) -> Result<Self, Self::Error> {
|
||||||
|
let droplets = split_lines(lines)
|
||||||
.map(|line| Droplet::parse(line))
|
.map(|line| Droplet::parse(line))
|
||||||
.try_collect()?;
|
.try_collect()?;
|
||||||
Ok(Blob { droplets })
|
Ok(Blob { droplets })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Blob {
|
||||||
fn extent(&self) -> Option<Ranges> {
|
fn extent(&self) -> Option<Ranges> {
|
||||||
if self.droplets.is_empty() {
|
if self.droplets.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -198,13 +203,13 @@ impl Blob {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(64);
|
let expected = ResultType::Integer(64);
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -215,7 +220,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Integer(58);
|
let expected = ResultType::Integer(58);
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::template::{DayTrait, ResultType};
|
use super::template::{DayTrait, ResultType};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
const DAY_NUMBER: usize = 0;
|
const DAY_NUMBER: usize = todo!();
|
||||||
|
|
||||||
pub struct Day;
|
pub struct Day;
|
||||||
|
|
||||||
|
|
@ -9,25 +10,31 @@ impl DayTrait for Day {
|
||||||
DAY_NUMBER
|
DAY_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part1(&self, _lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
Ok(ResultType::Nothing)
|
Ok(ResultType::Nothing)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(&self, _lines: &[String]) -> anyhow::Result<ResultType> {
|
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||||
Ok(ResultType::Nothing)
|
Ok(ResultType::Nothing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum DayError {
|
||||||
|
#[error("Dummy")]
|
||||||
|
Dummy,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::file::read_lines;
|
use crate::common::file::read_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part1() -> Result<()> {
|
fn test_part1() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Nothing;
|
let expected = ResultType::Nothing;
|
||||||
let result = day.part1(&lines)?;
|
let result = day.part1(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -38,7 +45,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_part2() -> Result<()> {
|
fn test_part2() -> Result<()> {
|
||||||
let day = Day {};
|
let day = Day {};
|
||||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||||
let expected = ResultType::Nothing;
|
let expected = ResultType::Nothing;
|
||||||
let result = day.part2(&lines)?;
|
let result = day.part2(&lines)?;
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,6 @@ pub enum ResultType {
|
||||||
|
|
||||||
pub trait DayTrait {
|
pub trait DayTrait {
|
||||||
fn get_day_number(&self) -> usize;
|
fn get_day_number(&self) -> usize;
|
||||||
fn part1(&self, lines: &[String]) -> Result<ResultType>;
|
fn part1(&self, lines: &str) -> Result<ResultType>;
|
||||||
fn part2(&self, lines: &[String]) -> Result<ResultType>;
|
fn part2(&self, lines: &str) -> Result<ResultType>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ mod days;
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use common::file::read_lines;
|
use common::file::read_string;
|
||||||
use days::{day_provider, DayTrait, ResultType};
|
use days::{day_provider, DayTrait, ResultType};
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
|
|
@ -47,7 +47,7 @@ fn output(day: usize, part: usize, result: ResultType, time: Duration) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_part(day: &dyn DayTrait, is_part1: bool, lines: &[String]) -> Result<Duration> {
|
fn run_part(day: &dyn DayTrait, is_part1: bool, lines: &str) -> Result<Duration> {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let result = if is_part1 {
|
let result = if is_part1 {
|
||||||
day.part1(lines)?
|
day.part1(lines)?
|
||||||
|
|
@ -70,7 +70,7 @@ fn run_part(day: &dyn DayTrait, is_part1: bool, lines: &[String]) -> Result<Dura
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(day: &dyn DayTrait, part1: bool, part2: bool) -> Result<Duration> {
|
fn run(day: &dyn DayTrait, part1: bool, part2: bool) -> Result<Duration> {
|
||||||
let lines = read_lines(day.get_day_number(), "input.txt")?;
|
let lines = read_string(day.get_day_number(), "input.txt")?;
|
||||||
let elapsed1 = if part1 {
|
let elapsed1 = if part1 {
|
||||||
run_part(day, true, &lines)?
|
run_part(day, true, &lines)?
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue