Add solution for 2022 day 09 part 2
This commit is contained in:
parent
7b2dc1d04c
commit
ed18a04dae
@ -12,7 +12,7 @@
|
||||
| 06 | ✓ | ✓ | [Rust] |
|
||||
| 07 | ✓ | ✓ | [Rust] |
|
||||
| 08 | ✓ | ✓ | [Rust] |
|
||||
| 09 | ✓ | | [Rust] |
|
||||
| 09 | ✓ | ✓ | [Rust] |
|
||||
| 10 | | | |
|
||||
| 11 | | | |
|
||||
| 12 | | | |
|
||||
|
8
2022/day-09/inputs/test2.txt
Normal file
8
2022/day-09/inputs/test2.txt
Normal file
@ -0,0 +1,8 @@
|
||||
R 5
|
||||
U 8
|
||||
L 8
|
||||
D 3
|
||||
R 17
|
||||
D 10
|
||||
L 25
|
||||
U 20
|
@ -1,6 +1,6 @@
|
||||
use std::{env, io};
|
||||
|
||||
use aoc_2022_09::{parse_input, Instruction, Position, VisitedMap};
|
||||
use aoc_2022_09::{parse_input, Rope, Visited};
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let infile_path = env::args().nth(1).expect("input file");
|
||||
@ -14,69 +14,18 @@ fn main() -> io::Result<()> {
|
||||
|
||||
fn solve(path: &str) -> io::Result<usize> {
|
||||
let instructions = parse_input(path)?;
|
||||
let mut rope = Rope::new(2);
|
||||
let mut tail_visited = Visited::new();
|
||||
|
||||
let mut head_pos = Position(0, 0);
|
||||
let mut tail_pos = Position(0, 0);
|
||||
let mut tail_visited = VisitedMap::new();
|
||||
|
||||
tail_visited.insert(tail_pos.clone(), 1);
|
||||
tail_visited.insert(rope.tail().unwrap().clone());
|
||||
|
||||
for instruction in instructions {
|
||||
(head_pos, tail_pos) =
|
||||
apply_instruction(&instruction, &head_pos, &tail_pos, &mut tail_visited);
|
||||
instruction.apply_to_rope(&mut rope, &mut tail_visited);
|
||||
}
|
||||
|
||||
Ok(tail_visited.len())
|
||||
}
|
||||
|
||||
fn apply_instruction(
|
||||
instruction: &Instruction,
|
||||
head_pos: &Position,
|
||||
tail_pos: &Position,
|
||||
tail_visited: &mut VisitedMap,
|
||||
) -> (Position, Position) {
|
||||
let mut tmp_head_pos = head_pos.clone();
|
||||
let mut tmp_tail_pos = tail_pos.clone();
|
||||
|
||||
let (change, amount) = match *instruction {
|
||||
Instruction::Up(dist) => (Position(1, 0), dist),
|
||||
Instruction::Right(dist) => (Position(0, 1), dist),
|
||||
Instruction::Left(dist) => (Position(0, -1), dist),
|
||||
Instruction::Down(dist) => (Position(-1, 0), dist),
|
||||
};
|
||||
|
||||
for _ in 1..=amount {
|
||||
tmp_head_pos += change.clone();
|
||||
|
||||
let dx = tmp_head_pos.0 - tmp_tail_pos.0;
|
||||
let dxa = dx.abs();
|
||||
let dy = tmp_head_pos.1 - tmp_tail_pos.1;
|
||||
let dya = dy.abs();
|
||||
let same_row = tmp_head_pos.0 == tmp_tail_pos.0;
|
||||
let same_col = tmp_head_pos.1 == tmp_tail_pos.1;
|
||||
let mut head_changed = false;
|
||||
|
||||
if (!same_row && !same_col) && (dxa > 1 || dya > 1) {
|
||||
let x = if dx.is_negative() { -1 } else { 1 };
|
||||
let y = if dy.is_negative() { -1 } else { 1 };
|
||||
tmp_tail_pos += Position(x, y);
|
||||
head_changed = true;
|
||||
} else if dxa > 1 || dya > 1 {
|
||||
tmp_tail_pos += change.clone();
|
||||
head_changed = true;
|
||||
}
|
||||
|
||||
if head_changed {
|
||||
tail_visited
|
||||
.entry(tmp_tail_pos.clone())
|
||||
.and_modify(|v| *v += 1)
|
||||
.or_insert(1);
|
||||
}
|
||||
}
|
||||
|
||||
(tmp_head_pos, tmp_tail_pos)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
47
2022/day-09/src/bin/part_two.rs
Normal file
47
2022/day-09/src/bin/part_two.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use std::{env, io};
|
||||
|
||||
use aoc_2022_09::{parse_input, Rope, Visited};
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let infile_path = env::args().nth(1).expect("input file");
|
||||
|
||||
let result = solve(&infile_path)?;
|
||||
|
||||
println!("{result}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn solve(path: &str) -> io::Result<usize> {
|
||||
let instructions = parse_input(path)?;
|
||||
let mut rope = Rope::new(10);
|
||||
let mut tail_visited = Visited::new();
|
||||
|
||||
tail_visited.insert(rope.tail().unwrap().clone());
|
||||
|
||||
for instruction in instructions {
|
||||
instruction.apply_to_rope(&mut rope, &mut tail_visited);
|
||||
}
|
||||
|
||||
Ok(tail_visited.len())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sample() {
|
||||
let result = solve("inputs/test.txt").unwrap();
|
||||
assert_eq!(1, result);
|
||||
|
||||
let result = solve("inputs/test2.txt").unwrap();
|
||||
assert_eq!(36, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn puzzle() {
|
||||
let result = solve("inputs/puzzle.txt").unwrap();
|
||||
assert_eq!(2273, result);
|
||||
}
|
||||
}
|
@ -1,32 +1,80 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::HashSet,
|
||||
fs::File,
|
||||
io::{self, BufRead, BufReader},
|
||||
ops::{Add, AddAssign},
|
||||
path::Path,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Position(pub isize, pub isize);
|
||||
pub type Visited = HashSet<Knot>;
|
||||
|
||||
impl Add for Position {
|
||||
type Output = Self;
|
||||
#[derive(Debug)]
|
||||
pub struct Rope(Vec<Knot>);
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 + rhs.0, self.1 + rhs.0)
|
||||
impl Rope {
|
||||
pub fn new(len: usize) -> Self {
|
||||
Self(vec![Knot::default(); len])
|
||||
}
|
||||
|
||||
pub fn move_head(&mut self, change: (isize, isize)) {
|
||||
let mut knots = self.0.iter_mut();
|
||||
|
||||
let mut next_a = knots.next();
|
||||
|
||||
next_a.as_mut().map(|head_knot| {
|
||||
head_knot.0 += change.0;
|
||||
head_knot.1 += change.1;
|
||||
});
|
||||
|
||||
let mut next_b = knots.next();
|
||||
|
||||
loop {
|
||||
if next_a.is_some() && next_b.is_some() {
|
||||
next_a.as_mut().map(|knot_a| {
|
||||
next_b.as_mut().map(|knot_b| {
|
||||
knot_b.follow(&knot_a);
|
||||
});
|
||||
});
|
||||
|
||||
next_a = next_b;
|
||||
next_b = knots.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tail(&self) -> Option<&Knot> {
|
||||
self.0.last()
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Position {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.0 += rhs.0;
|
||||
self.1 += rhs.1;
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Knot(isize, isize);
|
||||
|
||||
impl Knot {
|
||||
pub fn follow(&mut self, other: &Self) {
|
||||
let dx = other.0 - self.0;
|
||||
let dxa = dx.abs();
|
||||
let dy = other.1 - self.1;
|
||||
let dya = dy.abs();
|
||||
let same_row = self.0 == other.0;
|
||||
let same_col = self.1 == other.1;
|
||||
|
||||
let x = if dx.is_negative() { -1 } else { 1 };
|
||||
let y = if dy.is_negative() { -1 } else { 1 };
|
||||
|
||||
if (!same_row && !same_col) && (dxa > 1 || dya > 1) {
|
||||
self.0 += x;
|
||||
self.1 += y;
|
||||
} else if dxa > 1 {
|
||||
self.0 += x;
|
||||
} else if dya > 1 {
|
||||
self.1 += y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type VisitedMap = HashMap<Position, u32>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Instruction {
|
||||
Up(isize),
|
||||
@ -59,6 +107,29 @@ impl FromStr for Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
pub fn apply_to_rope(&self, rope: &mut Rope, tail_visited: &mut Visited) {
|
||||
let (change, amount) = match *self {
|
||||
Instruction::Up(dist) => ((1, 0), dist),
|
||||
Instruction::Right(dist) => ((0, 1), dist),
|
||||
Instruction::Left(dist) => ((0, -1), dist),
|
||||
Instruction::Down(dist) => ((-1, 0), dist),
|
||||
};
|
||||
|
||||
for _ in 0..amount {
|
||||
let old_tail = rope.tail().unwrap().clone();
|
||||
|
||||
rope.move_head(change);
|
||||
|
||||
let new_tail = rope.tail().unwrap();
|
||||
|
||||
if old_tail != *new_tail {
|
||||
tail_visited.insert(new_tail.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_input<P>(path: P) -> io::Result<Vec<Instruction>>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
|
Loading…
Reference in New Issue
Block a user