Loading lizard...


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.


You must log in to comment.