use nom::{ branch::alt, bytes::complete::tag, character::{ complete::{anychar, line_ending, satisfy}, is_alphabetic, }, combinator::{map, map_opt}, multi::{many1, separated_list1}, IResult, }; pub mod inputs; mod test; type Int = u32; type Line = (Vec, Vec); fn parse_single_digit(input: &str) -> IResult<&str, Int> { map_opt(anychar, |i: char| i.to_digit(10))(input) } fn parse_single_digit_or_word(input: &str) -> IResult<&str, Int> { let (_, x) = alt(( map(tag("one"), |_| 1), map(tag("two"), |_| 2), map(tag("three"), |_| 3), map(tag("four"), |_| 4), map(tag("five"), |_| 5), map(tag("six"), |_| 6), map(tag("seven"), |_| 7), map(tag("eight"), |_| 8), map(tag("nine"), |_| 9), parse_single_digit, ))(input)?; let n = input.get(1..).unwrap_or(""); Ok((n, x)) } fn parse_line_sub( f: impl FnMut(&str) -> IResult<&str, Int>, input: &str, ) -> IResult<&str, Vec>> { many1(alt(( map(f, Some), map(satisfy(|c| is_alphabetic(c as u8)), |_| None), )))(input) } fn parse_line(input: &str) -> IResult<&str, Line> { let (_, i) = parse_line_sub(parse_single_digit, input)?; let i: Vec = i.into_iter().flatten().collect(); let (out, j) = parse_line_sub(parse_single_digit_or_word, input)?; let j: Vec = j.into_iter().flatten().collect(); Ok((out, (i, j))) } fn parse_input(input: &str) -> IResult<&str, Vec> { let (res, out) = separated_list1(line_ending, parse_line)(input)?; Ok((res, out)) } pub fn solution(input: &str) -> Result<(Int, Int), String> { let (_, input) = parse_input(input).map_err(|e| e.to_string())?; let (a, b): (Int, Int) = input .into_iter() .map(|(ads, bds)| { let f = |ds: Vec| { if ds.is_empty() { 0 } else { let n = ds.len() - 1; (ds[0] * 10) + ds[n] } }; (f(ads), f(bds)) }) .fold((0, 0), |(a, b), (c, d)| (a + c, b + d)); Ok((a, b)) }