day09 finished
This commit is contained in:
parent
a2f7808f6d
commit
f630ef6874
5 changed files with 2171 additions and 1 deletions
152
src/days/day09/mod.rs
Normal file
152
src/days/day09/mod.rs
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::{direction::Direction, pos::Pos};
|
||||
use std::{collections::HashSet, num::ParseIntError};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 9;
|
||||
|
||||
pub struct Day;
|
||||
|
||||
impl DayTrait for Day {
|
||||
fn get_day_number(&self) -> usize {
|
||||
DAY_NUMBER
|
||||
}
|
||||
|
||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||
let commands = parse_commands(lines)?;
|
||||
let mut rope = Rope::create(2);
|
||||
let result = rope.run(&commands);
|
||||
Ok(ResultType::Integer(result))
|
||||
}
|
||||
|
||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||
let commands = parse_commands(lines)?;
|
||||
let mut rope = Rope::create(10);
|
||||
let result = rope.run(&commands);
|
||||
Ok(ResultType::Integer(result))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum RopeError {
|
||||
#[error("Not a legal command {0}")]
|
||||
CommandUnknown(String),
|
||||
|
||||
#[error["not a legal Direction {0}"]]
|
||||
DirectionUnknown(String),
|
||||
|
||||
#[error("Not a legal Integer")]
|
||||
StepParseError(#[from] ParseIntError),
|
||||
}
|
||||
|
||||
fn parse_commands(lines: &[String]) -> Result<Vec<(Direction, usize)>, RopeError> {
|
||||
lines.iter().map(parse_line).collect::<Result<_, _>>()
|
||||
}
|
||||
|
||||
fn parse_line(line: &String) -> Result<(Direction, usize), RopeError> {
|
||||
match line.split_whitespace().collect::<Vec<_>>()[..] {
|
||||
[direction, steps] => {
|
||||
let direction = match direction {
|
||||
"U" => Direction::North,
|
||||
"D" => Direction::South,
|
||||
"R" => Direction::East,
|
||||
"L" => Direction::West,
|
||||
_ => {
|
||||
return Err(RopeError::DirectionUnknown(direction.to_owned()));
|
||||
}
|
||||
};
|
||||
let steps = steps.parse()?;
|
||||
Ok((direction, steps))
|
||||
}
|
||||
_ => Err(RopeError::CommandUnknown(line.to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_closer(first: Pos<i32>, second: Pos<i32>) -> Option<Pos<i32>> {
|
||||
let diff = second - first;
|
||||
if diff.x().abs() <= 1 && diff.y().abs() <= 1 {
|
||||
None
|
||||
} else {
|
||||
Some(Pos::new(diff.x().signum(), diff.y().signum()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Rope {
|
||||
head: Pos<i32>,
|
||||
knots: Vec<Pos<i32>>,
|
||||
}
|
||||
|
||||
impl Rope {
|
||||
pub fn create(length: usize) -> Self {
|
||||
assert!(length >= 2);
|
||||
Rope {
|
||||
head: Pos::new(0, 0),
|
||||
knots: vec![Pos::new(0, 0); length - 1],
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the head one step into the given direction
|
||||
* returns true if the whole rope moved, otherwise false
|
||||
*/
|
||||
pub fn move_rope(&mut self, head_direction: Direction) -> bool {
|
||||
self.head = self.head + head_direction;
|
||||
let mut prev = self.head;
|
||||
let mut knots_unmoved = self.knots.len();
|
||||
|
||||
for knot in self.knots.iter_mut() {
|
||||
if let Some(diff) = get_closer(*knot, prev) {
|
||||
*knot = *knot + diff;
|
||||
prev = *knot;
|
||||
knots_unmoved -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
knots_unmoved == 0
|
||||
}
|
||||
|
||||
pub fn run(&mut self, commands: &[(Direction, usize)]) -> i64 {
|
||||
let mut tail_visited = HashSet::new();
|
||||
tail_visited.insert(self.knots.last().copied().unwrap());
|
||||
|
||||
for (direction, steps) in commands {
|
||||
for _ in 0..*steps {
|
||||
if self.move_rope(*direction) {
|
||||
tail_visited.insert(self.knots.last().copied().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
tail_visited.len() as i64
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::common::file::read_lines;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_part1() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(13);
|
||||
let result = day.part1(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example02.txt")?;
|
||||
let expected = ResultType::Integer(36);
|
||||
let result = day.part2(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ mod day05;
|
|||
mod day06;
|
||||
mod day07;
|
||||
mod day08;
|
||||
mod day09;
|
||||
mod template;
|
||||
|
||||
pub use template::DayTrait;
|
||||
|
|
@ -15,7 +16,7 @@ pub mod day_provider {
|
|||
use super::*;
|
||||
use thiserror::Error;
|
||||
|
||||
const MAX_DAY: usize = 8;
|
||||
const MAX_DAY: usize = 9;
|
||||
|
||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||
match day_num {
|
||||
|
|
@ -27,6 +28,7 @@ pub mod day_provider {
|
|||
6 => Ok(Box::new(day06::Day)),
|
||||
7 => Ok(Box::new(day07::Day)),
|
||||
8 => Ok(Box::new(day08::Day)),
|
||||
9 => Ok(Box::new(day09::Day)),
|
||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue