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.
aoc2022/day14/src/main.rs

251 lines
5.2 KiB

#![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: ?Sized>(I);
impl<I: ?Sized, T> Iterator for TakeTwo<I>
where I: Iterator<Item=T>
{
type Item = (T, Option<T>);
#[inline]
fn next(&mut self) -> Option<Self::Item>
{
let first = self.0.next()?;
Some((first, self.0.next()))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (low, high) = self.0.size_hint();
(low / 2, high.map(|x| x/2))
}
}
impl<I: ?Sized + ExactSizeIterator> ExactSizeIterator for TakeTwo<I>{}
impl<I: ?Sized + FusedIterator> FusedIterator for TakeTwo<I> {}
pub trait TakeTwoExt: Sized {
fn take_two(self) -> TakeTwo<Self>;
}
pub trait TakeTwoBoxedExt {
fn take_two(self: Box<Self>) -> TakeTwo<Box<Self>>;
}
impl<I: Iterator> TakeTwoExt for I
{
#[inline(always)]
fn take_two(self) -> TakeTwo<Self> {
TakeTwo(self)
}
}
impl<I: ?Sized + Iterator> TakeTwoBoxedExt for I
{
#[inline(always)]
fn take_two(self: Box<Self>) -> TakeTwo<Box<Self>> {
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 = <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)]
struct Line {
start: Point,
end: Point,
}
/// A single parsed line into `Line`s.
#[derive(Debug, Clone, Default)]
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 Lines {
#[inline]
pub fn concat(mut self, next: impl IntoIterator<Item = Line>) -> Self
{
self.lines.extend(next);
self
}
#[inline]
pub fn flatten(lines: impl IntoIterator<Item = Self>) -> Self
{
Self {
lines: lines.into_iter().map(|x| x.lines.into_iter()).flatten().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))
}
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()
}
}
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 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(())
}