2022 - Day 9
This commit is contained in:
parent
b42678d356
commit
e1a993e56b
2000
2022/input/9/input
Normal file
2000
2022/input/9/input
Normal file
File diff suppressed because it is too large
Load Diff
8
2022/input/9/test-1
Normal file
8
2022/input/9/test-1
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
R 4
|
||||||
|
U 4
|
||||||
|
L 3
|
||||||
|
D 1
|
||||||
|
R 4
|
||||||
|
D 1
|
||||||
|
L 5
|
||||||
|
R 2
|
8
2022/input/9/test-2
Normal file
8
2022/input/9/test-2
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
R 5
|
||||||
|
U 8
|
||||||
|
L 8
|
||||||
|
D 3
|
||||||
|
R 17
|
||||||
|
D 10
|
||||||
|
L 25
|
||||||
|
U 20
|
222
2022/src/bin/day9.rs
Normal file
222
2022/src/bin/day9.rs
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
#![feature(test)]
|
||||||
|
use core::fmt;
|
||||||
|
use std::{collections::HashSet, str::FromStr};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use aoc::Solver;
|
||||||
|
|
||||||
|
// -- Runners --
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
Day::solve()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_test1() -> Result<()> {
|
||||||
|
Day::test(aoc::Part::ONE, "test-1", 13)
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn part1_solution() -> Result<()> {
|
||||||
|
Day::test(aoc::Part::ONE, "input", 5695)
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn part2_test1() -> Result<()> {
|
||||||
|
Day::test(aoc::Part::TWO, "test-1", 1)
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn part2_test2() -> Result<()> {
|
||||||
|
Day::test(aoc::Part::TWO, "test-2", 36)
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn part2_solution() -> Result<()> {
|
||||||
|
Day::test(aoc::Part::TWO, "input", 2434)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmarks
|
||||||
|
extern crate test;
|
||||||
|
#[bench]
|
||||||
|
#[ignore]
|
||||||
|
fn part1_bench(b: &mut test::Bencher) {
|
||||||
|
Day::benchmark(aoc::Part::ONE, b)
|
||||||
|
}
|
||||||
|
#[bench]
|
||||||
|
#[ignore]
|
||||||
|
fn part2_bench(b: &mut test::Bencher) {
|
||||||
|
Day::benchmark(aoc::Part::TWO, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
struct Position {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
fn new(x: i32, y: i32) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the distance apart on either axis and then returns the largest
|
||||||
|
fn distance(&self, other: &Self) -> i32 {
|
||||||
|
std::cmp::max((self.x - other.x).abs(), (self.y - other.y).abs())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Right,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Direction {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"U" => Ok(Direction::Up),
|
||||||
|
"R" => Ok(Direction::Right),
|
||||||
|
"D" => Ok(Direction::Down),
|
||||||
|
"L" => Ok(Direction::Left),
|
||||||
|
_ => Err(anyhow::anyhow!("Invalid input")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Rope {
|
||||||
|
elements: Vec<Position>,
|
||||||
|
visited: HashSet<Position>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rope {
|
||||||
|
fn new(length: usize) -> Self {
|
||||||
|
let start = Position::new(0, 0);
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
visited.insert(start);
|
||||||
|
|
||||||
|
let mut elements = Vec::new();
|
||||||
|
for _ in 0..length {
|
||||||
|
elements.push(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {elements, visited}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(&mut self, d: Direction) {
|
||||||
|
// Get a &mut to the first element
|
||||||
|
let mut head = self.elements.first_mut().unwrap();
|
||||||
|
|
||||||
|
// Update the first element
|
||||||
|
match d {
|
||||||
|
Direction::Up => head.y += 1,
|
||||||
|
Direction::Right => head.x += 1,
|
||||||
|
Direction::Down => head.y -= 1,
|
||||||
|
Direction::Left => head.x -= 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head is now a copy of the position of the first element
|
||||||
|
let mut head = *head;
|
||||||
|
|
||||||
|
// Update all remaining elements of the rope
|
||||||
|
for tail in self.elements.iter_mut().skip(1) {
|
||||||
|
// Check if tail is touching the head
|
||||||
|
if tail.distance(&head) > 1 {
|
||||||
|
if tail.x < head.x {
|
||||||
|
tail.x += 1;
|
||||||
|
} else if tail.x > head.x {
|
||||||
|
tail.x -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if tail.y < head.y {
|
||||||
|
tail.y += 1;
|
||||||
|
} else if tail.y > head.y {
|
||||||
|
tail.y -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
head = *tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the location of the last element as visited
|
||||||
|
self.visited.insert(*self.elements.last().unwrap());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Rope {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
// @TODO Automatically pick the right value for this
|
||||||
|
let xmin = -15;
|
||||||
|
let xmax = 15;
|
||||||
|
let ymin = -10;
|
||||||
|
let ymax = 10;
|
||||||
|
|
||||||
|
for y in (ymin..ymax).rev() {
|
||||||
|
for x in xmin..xmax {
|
||||||
|
let pos = Position::new(x, y);
|
||||||
|
let c;
|
||||||
|
if *self.elements.first().unwrap() == pos {
|
||||||
|
c = 'H';
|
||||||
|
} else if self.elements.contains(&pos) {
|
||||||
|
let index = self.elements.iter().position(|p| *p == pos).unwrap();
|
||||||
|
c = char::from(index as u8 + b'0');
|
||||||
|
} else if self.visited.contains(&pos) {
|
||||||
|
c = '#';
|
||||||
|
} else {
|
||||||
|
c = '.'
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}", c)?;
|
||||||
|
}
|
||||||
|
writeln!(f, "")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(f, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(input: &str) -> Vec<(Direction, usize)> {
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.map(|line| line.split_once(" ").unwrap())
|
||||||
|
.map(|(d, c)| (d.parse().unwrap(), c.parse().unwrap()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solution(input: &str, length: usize) -> usize {
|
||||||
|
let instructions = parse(input);
|
||||||
|
|
||||||
|
let mut rope = Rope::new(length);
|
||||||
|
|
||||||
|
for instruction in instructions {
|
||||||
|
for _ in 0..instruction.1 {
|
||||||
|
rope.step(instruction.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rope.visited.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Solution --
|
||||||
|
pub struct Day;
|
||||||
|
impl aoc::Solver for Day {
|
||||||
|
type Output = usize;
|
||||||
|
fn day() -> u8 {
|
||||||
|
9
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(input: &str) -> Self::Output {
|
||||||
|
solution(input, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: &str) -> Self::Output {
|
||||||
|
solution(input, 10)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user