#![warn(clippy::pedantic)] use std::{collections::HashSet, io::stdin}; use aoc::vec2::{Line, Vec2}; use itertools::Itertools; fn parse_points(s: &str) -> impl Iterator + '_ { 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, floor: Option) -> 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 = stdin() .lines() .flat_map(|line| { parse_points(&line.unwrap()) .tuple_windows() .map(|(start, end)| Line { start, end }) .collect::>() }) .flat_map(|line| line.points().unwrap()) .collect(); println!("{}", simulate(points.clone(), None)); println!("{}", simulate(points, Some(2))); }