#![feature(repr_simd)] #[macro_use] extern crate log; use std::{ str, io, iter, path::Path, fmt, }; use color_eyre::{ eyre::{ self, eyre, WrapErr as _, }, Help as _, SectionExt as _, }; use linebuffer::prelude::*; #[cfg(feature="jemallocator")] const _:() = { use jemallocator::Jemalloc; #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; }; mod grid; /// Base coordinate pub type Coord = u64; /// A point is a vector #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)] #[repr(simd)] pub struct Point { pub x: Coord, pub y: Coord, } impl fmt::Display for Point { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{},{}", self.x, self.y) } } 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)] pub struct Line { start: Point, end: Point, } impl fmt::Display for Line { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} -> {}", self.start, self.end) } } impl From for (Coord, Coord) { #[inline] fn from(from: Point) -> Self { (from.x, from.y) } } impl From<(Coord, Coord)> for Point { #[inline(always)] fn from((x,y): (Coord, Coord)) -> Self { Self {x,y} } } impl Line { fn normalised(self) -> Self { let x = std::cmp::min(self.start.x, self.end.x); let y = std::cmp::min(self.start.y, self.end.y); let start = Point {x,y}; let x = std::cmp::max(self.start.x, self.end.x); let y = std::cmp::max(self.start.y, self.end.y); let end = Point {x,y}; Self { start, end } } #[inline] fn normalise(&mut self) { *self = self.clone().normalised(); } #[inline] pub fn over_horizontal(&self) -> impl IntoIterator + 'static { let x = self.start.x; let y = self.start.y; (self.start.x..=self.end.x).map(move |x| (x, y).into()) } #[inline] pub fn over_vertical(&self) -> impl IntoIterator + 'static { let x = self.start.x; (self.start.y..=self.end.y).map(move |y| (x, y).into()) } #[inline] pub fn unroll(&self) -> impl IntoIterator + 'static { self.over_horizontal().into_iter() .chain(self.over_vertical().into_iter()) } } /// A single parsed line into `Line`s. #[derive(Debug, Clone, Default)] pub 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 FromIterator for Lines { #[inline] fn from_iter>(lines: I) -> Self { Self { lines: Self::flattening(lines).into_iter().collect(), } } } impl FromIterator for Lines { #[inline] fn from_iter>(lines: I) -> Self { Self { lines: lines.into_iter().collect() } } } impl Lines { #[inline] pub fn concat(mut self, next: impl IntoIterator) -> Self { self.lines.extend(next); self } #[inline(always)] pub fn flattening<'a, T: 'a>(lines: impl IntoIterator + 'a) -> impl IntoIterator + 'a where T: IntoIterator { lines.into_iter().map(|x| x.into_iter()).flatten() } #[inline] pub fn flatten(lines: impl IntoIterator) -> Self { lines.into_iter().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)) } #[inline] 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() } } impl iter::FusedIterator for ParsedIter where ParsedLines>: iter::FusedIterator{} 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 init() -> eyre::Result<()> { color_eyre::install()?; #[cfg(feature="status")] pretty_env_logger::init(); Ok(()) } fn draw_grid(mut to: W, bound: Line, grid: &grid::Grid) -> io::Result<()> { for cells in grid.iterate_sparse(bound).chunk((bound.end.x - bound.start.x) as usize) { for cell in cells { write!(&mut to, "{}", cell)?; } write!(&mut to, "\n")?; } Ok(()) } /// Simulate the grid falling from `origin` in `grid`. fn simulate(grid: &mut grid::Grid, origin: &Point) -> eyre::Result<()> { Ok(()) } fn main() -> eyre::Result<()> { init().wrap_err("Failed to install handlers and hooks")?; // Parsed input let input = { // Input file let input = load_input_from(INPUT_FILENAME) .wrap_err("Failed to open input file")?; // Parse input Input::parse(input) .wrap_err("Failed to parse input")? }; if cfg!(feature="status") { debug!("Read {} zipped lines from input", input.zipped_len()); trace!("Input is:\n```{input:#?}```"); } //Draw lines into grid before starting simulation of sand from `origin`. let (mut grid, origin) = grid::sparse_grid(input.all); debug!("Read {} unzipped lines", grid.len()); //TODO: Simulate falling, adding and counting each stopped particle until the sand drops below the bottom of the lowest vertical line. simulate(&mut grid, &origin) .wrap_err("Failed to simulate grid")?; // Draw grid if cfg!(feature="status") { let bound = grid.find_bound(); debug!("Current map (bound: {}):", bound); draw_grid(io::stderr().lock(), bound, &grid).wrap_err("Failed to draw grid to stderr")?; } Ok(()) }