day09 finished
This commit is contained in:
parent
a2f7808f6d
commit
f630ef6874
5 changed files with 2171 additions and 1 deletions
8
data/day09/example01.txt
Normal file
8
data/day09/example01.txt
Normal 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
8
data/day09/example02.txt
Normal 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
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
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 day06;
|
||||||
mod day07;
|
mod day07;
|
||||||
mod day08;
|
mod day08;
|
||||||
|
mod day09;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
pub use template::DayTrait;
|
pub use template::DayTrait;
|
||||||
|
|
@ -15,7 +16,7 @@ pub mod day_provider {
|
||||||
use super::*;
|
use super::*;
|
||||||
use thiserror::Error;
|
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> {
|
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||||
match day_num {
|
match day_num {
|
||||||
|
|
@ -27,6 +28,7 @@ pub mod day_provider {
|
||||||
6 => Ok(Box::new(day06::Day)),
|
6 => Ok(Box::new(day06::Day)),
|
||||||
7 => Ok(Box::new(day07::Day)),
|
7 => Ok(Box::new(day07::Day)),
|
||||||
8 => Ok(Box::new(day08::Day)),
|
8 => Ok(Box::new(day08::Day)),
|
||||||
|
9 => Ok(Box::new(day09::Day)),
|
||||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue