solve year 2023 day 06 & rewrite number parsing to be generic
This commit is contained in:
parent
46165b1acb
commit
f2506f91ba
2
input/year2023/day06.txt
Normal file
2
input/year2023/day06.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Time: 51 92 68 90
|
||||
Distance: 222 2031 1126 1225
|
@ -25,4 +25,5 @@ pub mod year2023 {
|
||||
pub mod day03;
|
||||
pub mod day04;
|
||||
pub mod day05;
|
||||
pub mod day06;
|
||||
}
|
||||
|
@ -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),
|
||||
]
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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(())
|
||||
|
@ -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()?])
|
||||
});
|
||||
|
||||
|
@ -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" => {
|
||||
|
@ -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()
|
||||
|
@ -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
49
src/year2023/day06.rs
Normal 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())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user