Uh oh, running behind quite a bit.
use std::collections::HashSet;
use std::io::stdin;
use std::ops::Add;
use std::str::FromStr;
use std::{env, thread};
use std::time::Duration;
use itertools::Itertools;
use crate::Direction::{Down, DownLeft, DownRight, Left, Right, Up, UpLeft, UpRight};
use crate::rect::Rect;
type Position = (i32, i32);
type Rope = Vec<Position>;
mod rect;
fn show_rope_state(rope: &Rope, viewport: Rect) {
let w = viewport.right - viewport.left;
let h = viewport.bottom - viewport.top;
let mut cells: Vec<char> = vec!['.'; (w*h) as usize];
for (idx, knot) in rope.iter().enumerate().rev() {
let x = knot.0 - viewport.left;
let y = knot.1 - viewport.top;
let i = (x+y*w) as usize;
cells[i] = idx.to_string().chars().next().unwrap();
}
for row in cells.chunks(w as usize) {
let row = String::from_iter(row.iter());
println!("{row}");
}
}
fn distance(a: Position, b: Position) -> i32 {
let dx = a.0 - b.0;
let dy = a.1 - b.1;
dx.abs().max(dy.abs())
}
#[derive(Clone, Copy)]
enum Direction {
Up, Down, Left, Right,
UpRight, UpLeft, DownRight, DownLeft
}
impl FromStr for Direction {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"U" => Up,
"D" => Down,
"L" => Left,
"R" => Right,
_ => Err(())?
})
}
}
impl Add<Direction> for Position {
type Output = Position;
fn add(self, rhs: Direction) -> Self::Output {
match rhs {
Up => (self.0, self.1 - 1),
Down => (self.0, self.1 + 1),
Left => (self.0 - 1, self.1),
Right => (self.0 + 1, self.1),
UpLeft => self + Up + Left,
UpRight => self + Up + Right,
DownLeft => self + Down + Left,
DownRight => self + Down + Right
}
}
}
fn move_rope(rope: Rope, direction: Direction) -> Rope {
let head = *rope.first().unwrap();
let mut new_rope = vec![head + direction];
for i in 1..rope.len() {
let knot = rope[i];
let parent = new_rope[i-1];
let dx = parent.0 - knot.0;
let dy = parent.1 - knot.1;
let new_knot = if dx.abs() >= 2 || dy.abs() >= 2 {
(knot.0 + dx.signum(), knot.1 + dy.signum())
}
else {
knot
};
new_rope.push(new_knot);
}
assert_eq!(10, new_rope.len());
new_rope
}
fn main() {
let mut rope_state = vec![(0, 0); 10];
let mut tail_positions: HashSet<Position> = HashSet::new();
let display = env::args().find(|s| s == "display").is_some();
let viewport = Rect {
top: -20,
bottom: 20,
left: -20,
right: 20
};
for line in stdin().lines().flatten() {
let (dir, count) = line.split_once(' ').unwrap();
let dir = Direction::from_str(dir).unwrap();
let count = count.parse::<i32>().unwrap();
for _ in 0..count {
rope_state = move_rope(rope_state, dir);
let tail = rope_state.last().unwrap();
tail_positions.insert(*tail);
if display {
show_rope_state(&rope_state, viewport);
println!();
thread::sleep(Duration::from_millis(100));
}
}
if display {
thread::sleep(Duration::from_secs(1));
}
}
let (&min_x, &min_y, &max_x, &max_y) = tail_positions
.iter()
.fold(
(&0, &0, &0, &0),
|(min_x, min_y, max_x, max_y), (x, y)| (
min_x.min(x),
min_y.min(y),
max_x.max(x),
max_y.max(y)
));
for y in min_y..=max_y {
for x in min_x..=max_x {
if tail_positions.contains(&(x, y)) {
print!("#");
}
else {
print!(".");
}
}
println!();
}
println!("{}", tail_positions.len());
}
I kinda had the rope physics wrong for a while but it turned out to be a lot simpler than I thought.