Skip to content

Commit

Permalink
feat: add part 2 day 9
Browse files Browse the repository at this point in the history
  • Loading branch information
saiteki-kai committed Mar 2, 2024
1 parent c04baa1 commit a9b3099
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 74 deletions.
1 change: 1 addition & 0 deletions 2022/src/days/day09/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod part1;
pub mod part2;
mod shared;
75 changes: 2 additions & 73 deletions 2022/src/days/day09/part1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use std::fmt;

use super::shared::{parse_moves, Direction, Point};

pub fn solve() {
let input: &str = include_str!("../../../inputs/day09");

Expand All @@ -13,23 +15,6 @@ pub fn solve() {
println!("{}", count);
}

fn parse_moves(input: &str) -> Vec<(Direction, i32)> {
input
.lines()
.map(|line| {
let (dir, step) = line.trim().split_once(' ').unwrap();
let direction = match dir {
"U" => Direction::U,
"L" => Direction::L,
"R" => Direction::R,
_ => Direction::D,
};

(direction, step.parse::<i32>().unwrap())
})
.collect()
}

fn count_tail_positions(moves: Vec<(Direction, i32)>) -> usize {
let start = Point::new(0, 0);
let mut visited: Vec<Point> = vec![start];
Expand All @@ -50,62 +35,6 @@ fn count_tail_positions(moves: Vec<(Direction, i32)>) -> usize {

visited.len()
}

#[derive(Clone, Debug)]
pub enum Direction {
U,
R,
L,
D,
}

#[derive(Clone, Copy, PartialEq)]
struct Point {
x: i32,
y: i32,
}

impl Point {
fn new(x: i32, y: i32) -> Point {
Point { x, y }
}

fn move_to(&mut self, direction: &Direction) {
match direction {
Direction::R => self.x += 1,
Direction::L => self.x -= 1,
Direction::D => self.y += 1,
Direction::U => self.y -= 1,
}
}

fn move_diagonal_to(&mut self, direction_x: &Direction, direction_y: &Direction) {
self.move_to(direction_x);
self.move_to(direction_y);
}

fn gradient(point1: &Point, point2: &Point) -> Point {
Point {
x: point2.x - point1.x,
y: point2.y - point1.y,
}
}

fn distance_to(&self, point: &Point) -> i32 {
(self.x - point.x).abs().max((self.y - point.y).abs())
}

fn is_adjacent_to(&self, point: &Point) -> bool {
self.distance_to(point) <= 1
}
}

impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}

struct Rope {
head: Point,
tail: Point,
Expand Down
138 changes: 137 additions & 1 deletion 2022/src/days/day09/part2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,142 @@
//
// Day 9: Rope Bridge - Part 2

use std::fmt;

use super::shared::{parse_moves, Direction, Point};

pub fn solve() {

let input: &str = include_str!("../../../inputs/day09");

let moves = parse_moves(input);
let count = count_tail_positions(moves);

println!("{}", count);
}

fn count_tail_positions(moves: Vec<(Direction, i32)>) -> usize {
let start = Point::new(0, 0);
let mut visited: Vec<Point> = vec![start];

let mut rope = Rope::new(start);

for m in moves {
println!("{:?}, {}", m.0, m.1);

for _ in 0..(m.1) {
rope.move_to(&m.0);

let tail = rope.knots.last().unwrap();

if !visited.contains(tail) {
visited.push(*tail)
}
}
}

visited.len()
}

#[derive(Clone)]
struct Rope {
knots: Vec<Point>,
}

impl fmt::Display for Rope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let body = self
.knots
.iter()
.map(|k| format!("({}, {})", k.x, k.y))
.collect::<Vec<String>>()
.join(", ");

write!(f, "{}", body)
}
}

impl Rope {
fn new(start: Point) -> Rope {
Rope {
knots: [start; 10].to_vec(),
}
}

fn move_to(&mut self, direction: &Direction) {
Rope::move_head(&mut self.knots[0], direction);

for i in 1..self.knots.len() {
let (head, tail) = self.knots.split_at_mut(i);
Rope::move_tail(&head[i - 1], &mut tail[0], direction);
}
println!("{}", self);
}

fn is_same_row_or_col(head: &Point, tail: &Point) -> bool {
head.y == tail.y || head.x == tail.x
}

fn is_adjacent(head: &Point, tail: &Point) -> bool {
head.is_adjacent_to(tail)
}

fn is_touching(head: &Point, tail: &Point) -> bool {
head == tail || Rope::is_adjacent(head, tail)
}

fn move_toward_to_head(tail: &mut Point, head: &Point) {
let p = Point::gradient(tail, head);

let dir_x = if p.x > 0 { Direction::R } else { Direction::L };
let dir_y = if p.y > 0 { Direction::D } else { Direction::U };

tail.move_diagonal_to(&dir_x, &dir_y);
}

fn move_head(head: &mut Point, direction: &Direction) {
head.move_to(direction);
}

fn move_tail(head: &Point, tail: &mut Point, direction: &Direction) {
// two steps directly up, down, left, or right from the tail
if tail.distance_to(head) == 2 && Rope::is_same_row_or_col(head, tail) {
tail.move_to(direction)
}

// the head and tail aren't touching and aren't in the same row or column
if !Rope::is_same_row_or_col(head, tail) && !Rope::is_touching(head, tail) {
Rope::move_toward_to_head(tail, head)
}
}
}

#[cfg(test)]
mod tests {
use super::{count_tail_positions, Direction};
use test_case::test_case;

#[test_case([
(Direction::R, 4),
(Direction::U, 4),
(Direction::L, 3),
(Direction::D, 1),
(Direction::R, 4),
(Direction::D, 1),
(Direction::L, 5),
(Direction::R, 2),
].to_vec(), 1)]
#[test_case([
(Direction::R, 5),
(Direction::U, 8),
(Direction::L, 8),
(Direction::D, 3),
(Direction::R, 17),
(Direction::D, 10),
(Direction::L, 25),
(Direction::U, 20),
].to_vec(), 36)]
fn test_moves(moves: Vec<(Direction, i32)>, value: usize) {
let count = count_tail_positions(moves);
assert_eq!(count, value);
}
}
73 changes: 73 additions & 0 deletions 2022/src/days/day09/shared.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use std::fmt;

#[derive(Clone, Debug)]
pub enum Direction {
U,
R,
L,
D,
}

pub fn parse_moves(input: &str) -> Vec<(Direction, i32)> {
input
.lines()
.map(|line| {
let (dir, step) = line.trim().split_once(' ').unwrap();
let direction = match dir {
"U" => Direction::U,
"L" => Direction::L,
"R" => Direction::R,
_ => Direction::D,
};

(direction, step.parse::<i32>().unwrap())
})
.collect()
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Point {
pub x: i32,
pub y: i32,
}

impl Point {
pub fn new(x: i32, y: i32) -> Point {
Point { x, y }
}

pub fn move_to(&mut self, direction: &Direction) {
match direction {
Direction::R => self.x += 1,
Direction::L => self.x -= 1,
Direction::D => self.y += 1,
Direction::U => self.y -= 1,
}
}

pub fn move_diagonal_to(&mut self, direction_x: &Direction, direction_y: &Direction) {
self.move_to(direction_x);
self.move_to(direction_y);
}

pub fn gradient(point1: &Point, point2: &Point) -> Point {
Point {
x: point2.x - point1.x,
y: point2.y - point1.y,
}
}

pub fn distance_to(&self, point: &Point) -> i32 {
(self.x - point.x).abs().max((self.y - point.y).abs())
}

pub fn is_adjacent_to(&self, point: &Point) -> bool {
self.distance_to(point) <= 1
}
}

impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}

0 comments on commit a9b3099

Please sign in to comment.