#![feature(repr_simd)] use std::{ str, io, iter::{ self, Fuse, }, path::Path, }; use color_eyre::{ eyre::{ self, eyre, WrapErr as _, }, Help as _, SectionExt as _, }; use linebuffer::{ FromBuf, TryFromBuf, ParsedLines, buf::forward, }; mod ext { use super::iter::{ self, FusedIterator, }; #[derive(Debug, Clone)] pub struct TakeTwo(I); impl Iterator for TakeTwo where I: Iterator { type Item = (T, Option); #[inline] fn next(&mut self) -> Option { let first = self.0.next()?; Some((first, self.0.next())) } #[inline] fn size_hint(&self) -> (usize, Option) { let (low, high) = self.0.size_hint(); (low / 2, high.map(|x| x/2)) } } impl ExactSizeIterator for TakeTwo{} impl FusedIterator for TakeTwo {} pub trait TakeTwoExt: Sized { fn take_two(self) -> TakeTwo; } pub trait TakeTwoBoxedExt { fn take_two(self: Box) -> TakeTwo>; } impl TakeTwoExt for I { #[inline(always)] fn take_two(self) -> TakeTwo { TakeTwo(self) } } impl TakeTwoBoxedExt for I { #[inline(always)] fn take_two(self: Box) -> TakeTwo> { TakeTwo(self) } } } use ext::{ TakeTwoExt as _, TakeTwoBoxedExt as _, }; /// A point is a vector #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)] #[repr(simd)] struct Point { pub x: u64, pub y: u64, } impl str::FromStr for Point { type Err = ::Err; #[inline] fn from_str(s: &str) -> Result { let (x, y) = s.split_once(',').unwrap_or((s, "0")); Ok(Self { x: x.parse()?, y: y.parse()?, }) } } /// A `Line` is potentially two X any Ys #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)] struct Line { start: Point, end: Point, } /// A single parsed line into `Line`s. #[derive(Debug, Clone, Default)] struct Lines { lines: Vec, } impl IntoIterator for Lines { type Item= Line; type IntoIter = std::vec::IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { self.lines.into_iter() } } impl Lines { #[inline] pub fn concat(mut self, next: impl IntoIterator) -> Self { self.lines.extend(next); self } #[inline] pub fn flatten(lines: impl IntoIterator) -> Self { Self { lines: lines.into_iter().map(|x| x.lines.into_iter()).flatten().collect(), } } } impl str::FromStr for Lines { type Err = ::Err; #[inline] fn from_str(s: &str) -> Result { let mut next = s.split(" -> ").peekable(); let mut lines = Vec::new(); while let Some(start) = next.next() { let Some(second) = next.peek() else { continue }; lines.push(Line { start: start.parse()?, end: second.parse()? }) } Ok(Self{lines}) } } #[derive(Debug)] struct Input { all: Lines, } impl Input { /// Size of all non-unzipped (H+V) lines #[inline] pub fn zipped_len(&self) -> usize { self.all.lines.len() } } impl FromIterator for Input { #[inline] fn from_iter>(iter: I) -> Self { Self{ all: Lines::flatten(iter.into_iter()) } } } impl Input { #[inline(always)] pub fn parse(reader: R) -> eyre::Result { Self::from_parser(ParsedLines::new(reader)) } pub fn from_parser(lines: ParsedLines>) -> eyre::Result { struct ParsedIter(ParsedLines>); impl Iterator for ParsedIter { type Item = Result>; #[inline(always)] fn next(&mut self) -> Option { self.0.try_next() } } ParsedIter(lines).enumerate().map(|(i,x)| x.wrap_err("Failed to parse single line").with_section(move || i.header("Line number is"))).collect() } } #[inline] fn load_input_from(from: impl AsRef) -> eyre::Result { std::fs::OpenOptions::new() .read(true) .open(&from) .with_section(|| format!("{:?}", from.as_ref()).header("File name was")) } /// Input filename const INPUT_FILENAME: &'static str = "input"; /// Starting point of sand falling const ORIGIN_POINT: Point = Point { x: 500, y: 0 }; fn main() -> eyre::Result<()> { color_eyre::install()?; // Input let input = load_input_from(INPUT_FILENAME) .wrap_err("Failed to open input file")?; // Parse input let input = Input::parse(input) .wrap_err("Failed to parse input")?; //TODO: Deconstruct each `Line` into the horizontal and vertical movements //eprintln!("{input:#?}"); println!("{}", input.zipped_len()); //TODO: Draw lines into grid before starting simulation of sand from `ORIGIN_POINT`. //TODO: Simulate falling, adding and counting each stopped particle until the sand drops below the bottom of the lowest vertical line. Ok(()) }