solve year 2023 day 06 & rewrite number parsing to be generic

This commit is contained in:
fuckwit 2023-12-06 08:10:19 +01:00
parent 46165b1acb
commit f2506f91ba
13 changed files with 174 additions and 61 deletions

2
input/year2023/day06.txt Normal file
View File

@ -0,0 +1,2 @@
Time: 51 92 68 90
Distance: 222 2031 1126 1225

View File

@ -25,4 +25,5 @@ pub mod year2023 {
pub mod day03;
pub mod day04;
pub mod day05;
pub mod day06;
}

View File

@ -12,8 +12,8 @@ struct Solution {
macro_rules! solution {
($year:tt, $day:tt) => {
Solution {
year: (&stringify!($year)).parse_u32(),
day: (&stringify!($day)).parse_u32(),
year: (&stringify!($year)).parse_unsigned::<u32>(),
day: (&stringify!($day)).parse_unsigned::<u32>(),
input: include_str!(concat![
"../input/",
stringify!($year),
@ -38,7 +38,7 @@ macro_rules! solution {
fn main() {
let (year, day) = match std::env::args().nth(1) {
Some(arg) => {
let mut split = arg.split("::").map(|s| s.parse_u32());
let mut split = arg.split("::").map(|s| s.parse_unsigned());
(split.next(), split.next())
}
None => (None, None),
@ -100,5 +100,6 @@ fn year2023() -> Vec<Solution> {
solution!(year2023, day03),
solution!(year2023, day04),
solution!(year2023, day05),
solution!(year2023, day06),
]
}

View File

@ -1,74 +1,133 @@
use std::marker::PhantomData;
use std::str::Bytes;
pub struct U32s<'a> {
pub trait IsSigned {}
impl IsSigned for i8 {}
impl IsSigned for i16 {}
impl IsSigned for i32 {}
impl IsSigned for i64 {}
impl IsSigned for i128 {}
impl IsSigned for isize {}
pub trait IsUnsigned {}
impl IsUnsigned for u8 {}
impl IsUnsigned for u16 {}
impl IsUnsigned for u32 {}
impl IsUnsigned for u64 {}
impl IsUnsigned for u128 {}
impl IsUnsigned for usize {}
pub struct Signed<'a, T = i64>
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<i32> + TryFrom<u8> + IsSigned,
{
bytes: Bytes<'a>,
_phantom: PhantomData<T>,
}
impl<'a> Iterator for U32s<'a> {
type Item = u32;
impl<'a, T> Iterator for Signed<'a, T>
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<i32> + TryFrom<u8> + IsSigned,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
try_parse_u32(&mut self.bytes)
try_parse_signed::<T>(&mut self.bytes)
}
}
pub struct I32s<'a> {
pub struct Unsigned<'a, T = u64>
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<u8> + IsUnsigned,
{
bytes: Bytes<'a>,
_phantom: PhantomData<T>,
}
impl<'a> Iterator for I32s<'a> {
type Item = i32;
impl<'a, T> Iterator for Unsigned<'a, T>
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<u8> + IsUnsigned,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
try_parse_i32(&mut self.bytes)
try_parse_unsigned::<T>(&mut self.bytes)
}
}
pub trait ParseExt {
fn parse_u32(&self) -> u32;
fn parse_signed<T>(&self) -> T
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<i32> + TryFrom<u8> + IsSigned;
fn u32s(&self) -> U32s<'_>;
fn parse_unsigned<T>(&self) -> T
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<u8> + IsUnsigned;
fn parse_i32(&self) -> i32;
fn signed<T>(&self) -> Signed<'_, T>
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<i32> + TryFrom<u8> + IsSigned;
fn i32s(&self) -> I32s<'_>;
fn unsigned<T>(&self) -> Unsigned<'_, T>
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<u8> + IsUnsigned;
}
impl ParseExt for &str {
fn parse_u32(&self) -> u32 {
match try_parse_u32(&mut self.bytes()) {
Some(num) => num,
None => panic!("unable to parse u32: {self}"),
}
}
fn u32s(&self) -> U32s<'_> {
U32s {
fn signed<T>(&self) -> Signed<'_, T>
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<i32> + TryFrom<u8> + IsSigned,
{
Signed {
bytes: self.bytes(),
_phantom: PhantomData,
}
}
fn parse_i32(&self) -> i32 {
match try_parse_i32(&mut self.bytes()) {
fn parse_signed<T>(&self) -> T
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<i32> + TryFrom<u8> + IsSigned,
{
match try_parse_signed(&mut self.bytes()) {
Some(num) => num,
None => panic!("unable to parse i32: {self}"),
None => panic!("unable to parse signed: {self}"),
}
}
fn i32s(&self) -> I32s<'_> {
I32s {
fn unsigned<T>(&self) -> Unsigned<'_, T>
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<u8> + IsUnsigned,
{
Unsigned {
bytes: self.bytes(),
_phantom: PhantomData,
}
}
fn parse_unsigned<T>(&self) -> T
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<u8> + IsUnsigned,
{
match try_parse_unsigned(&mut self.bytes()) {
Some(num) => num,
None => panic!("unable to parse signed: {self}"),
}
}
}
fn try_parse_u32(bytes: &mut Bytes<'_>) -> Option<u32> {
use std::ops::{Add, Mul};
fn try_parse_unsigned<T>(bytes: &mut Bytes<'_>) -> Option<T>
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<u8> + IsUnsigned,
{
let mut n = loop {
let byte = bytes.next()?;
let digit = byte.wrapping_sub(b'0');
if digit < 10 {
break digit as u32;
break T::try_from(digit).unwrap_or_else(|_| unreachable!());
}
};
@ -79,29 +138,33 @@ fn try_parse_u32(bytes: &mut Bytes<'_>) -> Option<u32> {
let digit = byte.wrapping_sub(b'0');
if digit < 10 {
n = 10 * n + digit as u32;
n = T::try_from(10).unwrap_or_else(|_| unreachable!()) * n
+ T::try_from(digit).unwrap_or_else(|_| unreachable!());
} else {
break Some(n);
}
}
}
fn try_parse_i32(bytes: &mut Bytes<'_>) -> Option<i32> {
let mut fac = 1;
fn try_parse_signed<T>(bytes: &mut Bytes<'_>) -> Option<T>
where
T: Add<Output = T> + Mul<Output = T> + TryFrom<i32> + TryFrom<u8> + IsSigned,
{
let mut fac = T::try_from(1i32).unwrap_or_else(|_| unreachable!());
let mut n = loop {
let byte = bytes.next()?;
if byte == b'-' {
let byte = bytes.next()?;
let digit = byte.wrapping_sub(b'0');
if digit < 10 {
fac = -1;
break digit as i32;
fac = T::try_from(-1i32).unwrap_or_else(|_| unreachable!());
break T::try_from(digit).unwrap_or_else(|_| unreachable!());
}
}
let digit = byte.wrapping_sub(b'0');
if digit < 10 {
break digit as i32;
break T::try_from(digit).unwrap_or_else(|_| unreachable!());
}
};
@ -112,7 +175,8 @@ fn try_parse_i32(bytes: &mut Bytes<'_>) -> Option<i32> {
let digit = byte.wrapping_sub(b'0');
if digit < 10 {
n = 10 * n + digit as i32;
n = T::try_from(10).unwrap_or_else(|_| unreachable!()) * n
+ T::try_from(digit).unwrap_or_else(|_| unreachable!());
} else {
break Some(n * fac);
}

View File

@ -4,7 +4,7 @@ use crate::util::{index::*, parse::ParseExt};
fn parse(input: &str) -> impl Iterator<Item = (u8, u32)> + '_ {
let dirs = input.bytes().filter(u8::is_ascii_uppercase);
let steps: Vec<_> = input.u32s().collect();
let steps: Vec<_> = input.unsigned().collect();
dirs.zip(steps)
}

View File

@ -9,7 +9,7 @@ pub fn part1(input: &str) -> impl std::fmt::Display {
input
.lines()
.map(|s| {
s.u32s()
s.unsigned()
// This is faster than `collect().try_into().expect("")`
// presumably because of the allocation of the Vec
// and the additional check for the Option for each line
@ -33,9 +33,9 @@ pub fn part2(input: &str) -> impl std::fmt::Display {
let mut valid = 0;
while let Some((l1, l2, l3)) = get3(&mut lines) {
let l1: Vec<u32> = l1.u32s().collect();
let l2: Vec<u32> = l2.u32s().collect();
let l3: Vec<u32> = l3.u32s().collect();
let l1: Vec<u32> = l1.unsigned().collect();
let l2: Vec<u32> = l2.unsigned().collect();
let l3: Vec<u32> = l3.unsigned().collect();
for i in 0..2 {
if valid_triangle(&[l1[i], l2[i], l3[i]]) {
valid += 1;

View File

@ -1,7 +1,7 @@
use crate::util::parse::ParseExt;
fn calories(input: &str) -> impl Iterator<Item = u32> + '_ {
input.split("\n\n").map(|set| set.u32s().sum())
input.split("\n\n").map(|set| set.unsigned::<u32>().sum())
}
pub fn part1(input: &str) -> impl std::fmt::Display {

View File

@ -4,7 +4,7 @@ fn solve(input: &str, filter: fn(u32, u32, u32, u32) -> bool) -> usize {
input
.lines()
.filter_map(|line| {
let mut nums = line.u32s();
let mut nums = line.unsigned::<u32>();
if (filter)(nums.next()?, nums.next()?, nums.next()?, nums.next()?) {
Some(())

View File

@ -2,7 +2,7 @@ use crate::util::parse::ParseExt;
fn parse(input: &str) -> Option<(Vec<Vec<char>>, impl Iterator<Item = [u32; 3]> + '_)> {
let (board, moveset) = input.split_once("\n\n")?;
let size = board.lines().last()?.u32s().count();
let size = board.lines().last()?.unsigned::<u32>().count();
let mut crates: Vec<Vec<char>> = vec![vec![]; size + 1];
board.lines().for_each(|line| {
@ -22,7 +22,7 @@ fn parse(input: &str) -> Option<(Vec<Vec<char>>, impl Iterator<Item = [u32; 3]>
}
let moves = moveset.lines().filter_map(|line| {
let mut nums = line.u32s();
let mut nums = line.unsigned();
Some([nums.next()?, nums.next()?, nums.next()?])
});

View File

@ -18,7 +18,7 @@ pub fn part1(input: &str) -> impl std::fmt::Display {
for b in draw.split(", ") {
let (num, col) = b.split_once(' ')?;
let num = num.parse_u32();
let num = num.parse_unsigned::<u32>();
match col {
"red" => red += num,
"green" => green += num,
@ -47,7 +47,7 @@ pub fn part2(input: &str) -> impl std::fmt::Display {
for draw in cubes.split("; ") {
for b in draw.split(", ") {
let (num, col) = b.split_once(' ')?;
let num = num.parse_u32();
let num = num.parse_unsigned::<u32>();
match col {
"red" => {

View File

@ -6,8 +6,8 @@ pub fn part1(input: &str) -> impl std::fmt::Display {
.map(|line| {
let (_, rest) = line.split_once(':').unwrap();
let (winning, numbers) = rest.split_once('|').unwrap();
let winning = winning.u32s().collect::<Vec<_>>();
let numbers = numbers.u32s().filter(|n| winning.contains(n)).count() as u32;
let winning = winning.unsigned::<u32>().collect::<Vec<_>>();
let numbers = numbers.unsigned::<u32>().filter(|n| winning.contains(n)).count() as u32;
(numbers > 0).then(|| 2u32.pow(numbers - 1)).unwrap_or(0)
})
.sum::<u32>()
@ -24,8 +24,8 @@ pub fn part2(input: &str) -> impl std::fmt::Display {
.map(|(i, line)| {
let (_, rest) = line.split_once(':').unwrap();
let (winning, numbers) = rest.split_once('|').unwrap();
let winning = winning.u32s().collect::<Vec<_>>();
let numbers = numbers.u32s().filter(|n| winning.contains(n)).count();
let winning = winning.unsigned::<u32>().collect::<Vec<_>>();
let numbers = numbers.unsigned::<u32>().filter(|n| winning.contains(n)).count();
let mul = mults[i];
mults
.iter_mut()

View File

@ -47,8 +47,7 @@ fn parse(input: &str) -> (Vec<usize>, Vec<Map>) {
let seeds = chunks
.next()
.unwrap()
.u32s()
.map(|v| v as usize)
.unsigned::<usize>()
.collect::<Vec<_>>();
let maps = chunks
@ -60,12 +59,8 @@ fn parse(input: &str) -> (Vec<usize>, Vec<Map>) {
if idx == 0 {
return None;
}
let mut nums = line.u32s();
Some((
nums.next()? as usize,
nums.next()? as usize,
nums.next()? as usize,
))
let mut nums = line.unsigned::<usize>();
Some((nums.next()?, nums.next()?, nums.next()?))
})
.collect(),
})
@ -110,6 +105,7 @@ pub fn part2(input: &str) -> impl std::fmt::Display {
res[0]
}
#[allow(dead_code)]
static TEST_INPUT: &str = "seeds: 79 14 55 13
seed-to-soil map:

49
src/year2023/day06.rs Normal file
View File

@ -0,0 +1,49 @@
use crate::util::parse::ParseExt;
pub fn part1(input: &str) -> impl std::fmt::Display {
let mut lines = input.lines();
let times = lines.next().unwrap();
let dists = lines.next().unwrap();
times
.signed::<i32>()
.zip(dists.signed())
.map(|(lim, dist)| {
(0..dist)
.filter_map(|d| (d * (lim - d) > dist).then_some(1))
.sum::<u32>()
})
.product::<u32>()
}
pub fn part2(input: &str) -> impl std::fmt::Display {
let mut lines = input.lines();
let time = lines
.next()
.unwrap()
.chars()
.filter(|a| a.is_ascii_digit())
.collect::<String>().as_str()
.parse_signed::<i128>();
let dist = lines
.next()
.unwrap()
.chars()
.filter(|a| a.is_ascii_digit())
.collect::<String>().as_str()
.parse_signed::<i128>();
(0..time)
.filter_map(|d| (d * (time - d) > dist).then_some(1))
.sum::<i128>()
}
#[test]
fn test_part1() {
assert_eq!("", part1("").to_string())
}
#[test]
fn test_part2() {
assert_eq!("", part2("").to_string())
}