day09 finished

This commit is contained in:
Ruediger Ludwig 2023-02-05 12:03:19 +01:00
parent a2f7808f6d
commit f630ef6874
5 changed files with 2171 additions and 1 deletions

8
data/day09/example01.txt Normal file
View file

@ -0,0 +1,8 @@
R 4
U 4
L 3
D 1
R 4
D 1
L 5
R 2

8
data/day09/example02.txt Normal file
View file

@ -0,0 +1,8 @@
R 5
U 8
L 8
D 3
R 17
D 10
L 25
U 20

2000
data/day09/input.txt Normal file

File diff suppressed because it is too large Load diff

152
src/days/day09/mod.rs Normal file
View 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(())
}
}

View file

@ -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)),
}
}