#![warn(clippy::pedantic)]

use std::{collections::HashSet, io::stdin};

use aoc::vec2::{Line, Vec2};
use itertools::Itertools;

fn parse_points(s: &str) -> impl Iterator<Item = Vec2> + '_ {
    s.split(" -> ").map(|p| {
        let (x, y) = p.split_once(',').unwrap();
        let x = x.parse().unwrap();
        let y = y.parse().unwrap();
        (x, y).into()
    })
}

fn simulate(structures: HashSet<Vec2>, floor: Option<usize>) -> usize {
    let mut points = structures;
    let lowest = points.iter().map(|v| v.y).max().unwrap();
    let start = Vec2::new(500, 0);

    let mut pieces = 0;
    'pieces: loop {
        let mut pos = start;
        'fall: loop {
            let new_pos = [(0, 1), (-1, 1), (1, 1)]
                .into_iter()
                .map(|direction| pos + direction.into())
                .find(|pos| !points.contains(pos));

            if let Some(new_pos) = new_pos {
                pos = new_pos;

                match floor {
                    Some(offset) => {
                        if pos.y >= lowest + i32::try_from(offset).unwrap() - 1 {
                            // landed on the floor
                            break 'fall;
                        }
                    }
                    None => {
                        if pos.y >= lowest {
                            // fell into the void
                            break 'pieces;
                        }
                    }
                }
            } else {
                // stuck
                break 'fall;
            }
        }

        points.insert(pos);
        pieces += 1;

        if pos == start {
            // all filled up
            break 'pieces;
        }
    }

    pieces
}

fn main() {
    let points: HashSet<Vec2> = stdin()
        .lines()
        .flat_map(|line| {
            parse_points(&line.unwrap())
                .tuple_windows()
                .map(|(start, end)| Line { start, end })
                .collect::<Vec<_>>()
        })
        .flat_map(|line| line.points().unwrap())
        .collect();

    println!("{}", simulate(points.clone(), None));
    println!("{}", simulate(points, Some(2)));
}
