Compare commits

...

2 Commits

12 changed files with 2275 additions and 2 deletions

4
2022/Cargo.lock generated
View File

@ -40,6 +40,10 @@ version = "0.1.0"
name = "aoc-2022-08" name = "aoc-2022-08"
version = "0.1.0" version = "0.1.0"
[[package]]
name = "aoc-2022-09"
version = "0.1.0"
[[package]] [[package]]
name = "either" name = "either"
version = "1.8.0" version = "1.8.0"

View File

@ -8,6 +8,7 @@ members = [
"./day-06", "./day-06",
"./day-07", "./day-07",
"./day-08", "./day-08",
"./day-09",
] ]

View File

@ -12,7 +12,7 @@
| 06 | ✓ | ✓ | [Rust] | | 06 | ✓ | ✓ | [Rust] |
| 07 | ✓ | ✓ | [Rust] | | 07 | ✓ | ✓ | [Rust] |
| 08 | ✓ | ✓ | [Rust] | | 08 | ✓ | ✓ | [Rust] |
| 09 | | | | | 09 | ✓ | ✓ | [Rust] |
| 10 | | | | | 10 | | | |
| 11 | | | | | 11 | | | |
| 12 | | | | | 12 | | | |

12
2022/day-09/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "aoc-2022-09"
version = "0.1.0"
edition = "2021"
[lib]
test = false
doctest = false
[dependencies]

5
2022/day-09/Justfile Normal file
View File

@ -0,0 +1,5 @@
@part PART INPUT_FILE="inputs/puzzle.txt":
cargo --quiet run --bin part_{{PART}} -- {{INPUT_FILE}}
clean:
cargo clean

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -0,0 +1,44 @@
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(2);
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!(13, result);
}
#[test]
fn puzzle() {
let result = solve("inputs/puzzle.txt").unwrap();
assert_eq!(6026, result);
}
}

View 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);
}
}

144
2022/day-09/src/lib.rs Normal file
View File

@ -0,0 +1,144 @@
use std::{
collections::HashSet,
fs::File,
io::{self, BufRead, BufReader},
path::Path,
str::FromStr,
};
pub type Visited = HashSet<Knot>;
#[derive(Debug)]
pub struct Rope(Vec<Knot>);
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()
}
}
#[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;
}
}
}
#[derive(Debug)]
pub enum Instruction {
Up(isize),
Right(isize),
Left(isize),
Down(isize),
}
impl FromStr for Instruction {
type Err = String;
fn from_str(val: &str) -> Result<Self, Self::Err> {
let parts = val.split_whitespace().collect::<Vec<&str>>();
let [direction, distance, ..] = parts[..] else {
return Err("Invalid instruction".to_string());
};
let distance = distance
.parse()
.map_err(|_| "Invalid distance value".to_string())?;
let result = match direction {
"U" => Self::Up(distance),
"R" => Self::Right(distance),
"L" => Self::Left(distance),
"D" => Self::Down(distance),
_ => return Err("Invalid direction value".to_string()),
};
Ok(result)
}
}
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>,
{
let file = File::open(path)?;
let instructions = BufReader::new(file)
.lines()
.map(|line| line.unwrap().parse().unwrap())
.collect();
Ok(instructions)
}

View File

@ -9,4 +9,4 @@
- [2019](2019/README.md) (0% completed) - [2019](2019/README.md) (0% completed)
- [2020](2020/README.md) (20% completed) - [2020](2020/README.md) (20% completed)
- [2021](2021/README.md) (68% completed) - [2021](2021/README.md) (68% completed)
- [2022](2022/README.md) (32% completed) - [2022](2022/README.md) (36% completed)