You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
360 lines
7.4 KiB
360 lines
7.4 KiB
#![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 = <u64 as str::FromStr>::Err;
|
|
#[inline]
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
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<Point> 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<Item = Point> + '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<Item = Point> + '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<Item = Point> + '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<Line>,
|
|
}
|
|
|
|
impl IntoIterator for Lines
|
|
{
|
|
type Item= Line;
|
|
type IntoIter = std::vec::IntoIter<Line>;
|
|
|
|
#[inline]
|
|
fn into_iter(self) -> Self::IntoIter
|
|
{
|
|
self.lines.into_iter()
|
|
}
|
|
}
|
|
|
|
impl FromIterator<Lines> for Lines
|
|
{
|
|
#[inline]
|
|
fn from_iter<I: IntoIterator<Item=Lines>>(lines: I) -> Self
|
|
{
|
|
Self {
|
|
lines: Self::flattening(lines).into_iter().collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromIterator<Line> for Lines
|
|
{
|
|
#[inline]
|
|
fn from_iter<I: IntoIterator<Item=Line>>(lines: I) -> Self
|
|
{
|
|
Self {
|
|
lines: lines.into_iter().collect()
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
impl Lines {
|
|
#[inline]
|
|
pub fn concat(mut self, next: impl IntoIterator<Item = Line>) -> Self
|
|
{
|
|
self.lines.extend(next);
|
|
self
|
|
}
|
|
#[inline(always)]
|
|
pub fn flattening<'a, T: 'a>(lines: impl IntoIterator<Item = T> + 'a) -> impl IntoIterator<Item = Line> + 'a
|
|
where T: IntoIterator<Item = Line>
|
|
|
|
{
|
|
lines.into_iter().map(|x| x.into_iter()).flatten()
|
|
}
|
|
#[inline]
|
|
pub fn flatten(lines: impl IntoIterator<Item = Self>) -> Self
|
|
{
|
|
lines.into_iter().collect()
|
|
}
|
|
}
|
|
|
|
impl str::FromStr for Lines
|
|
{
|
|
type Err = <Point as str::FromStr>::Err;
|
|
#[inline]
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
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<Lines> for Input
|
|
{
|
|
#[inline]
|
|
fn from_iter<I: IntoIterator<Item=Lines>>(iter: I) -> Self
|
|
{
|
|
Self{ all: Lines::flatten(iter.into_iter()) }
|
|
}
|
|
}
|
|
|
|
|
|
impl Input {
|
|
#[inline(always)]
|
|
pub fn parse<R: io::Read>(reader: R) -> eyre::Result<Self>
|
|
{
|
|
Self::from_parser(ParsedLines::new(reader))
|
|
}
|
|
#[inline]
|
|
pub fn from_parser<R: io::Read>(lines: ParsedLines<R, forward::FromStr<Lines>>) -> eyre::Result<Self>
|
|
{
|
|
struct ParsedIter<T: io::Read>(ParsedLines<T, forward::FromStr<Lines>>);
|
|
impl<T: io::Read> Iterator for ParsedIter<T>
|
|
{
|
|
type Item = Result<Lines, linebuffer::lines::LineParseError<std::num::ParseIntError>>;
|
|
#[inline(always)]
|
|
fn next(&mut self) -> Option<Self::Item>
|
|
{
|
|
self.0.try_next()
|
|
}
|
|
}
|
|
impl<T: io::Read> iter::FusedIterator for ParsedIter<T>
|
|
where ParsedLines<T, forward::FromStr<Lines>>: 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<Path>) -> eyre::Result<impl io::Read + 'static>
|
|
{
|
|
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<W: io::Write>(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(())
|
|
}
|