switch to nom
This commit is contained in:
parent
a5f19ecae1
commit
b00835c25e
7 changed files with 288 additions and 181 deletions
|
|
@ -1,9 +1,19 @@
|
|||
use crate::common::file::split_lines;
|
||||
use crate::common::parser::{
|
||||
empty_lines, eol_terminated, extract_result, ignore, trim0, trim_left1, true_false,
|
||||
};
|
||||
|
||||
use super::template::{DayTrait, ResultType};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::{iter::zip, num::ParseIntError};
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete::{char, i64, u32},
|
||||
combinator::{map, value, verify},
|
||||
error::Error,
|
||||
multi::{many1, separated_list1},
|
||||
sequence::{preceded, tuple},
|
||||
Err, IResult, Parser,
|
||||
};
|
||||
use std::{iter::zip, str::FromStr};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 11;
|
||||
|
|
@ -16,50 +26,50 @@ impl DayTrait for Day {
|
|||
}
|
||||
|
||||
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||
let troop = Troop::try_from(lines)?;
|
||||
let troop: Troop = lines.parse()?;
|
||||
Ok(ResultType::Integer(troop.play(20)))
|
||||
}
|
||||
|
||||
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||
let troop = Troop::try_from(lines)?;
|
||||
let troop: Troop = lines.parse()?;
|
||||
Ok(ResultType::Integer(troop.play_again(10_000)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum MonkeyError {
|
||||
#[error("Not an Integer")]
|
||||
NotAnInteger(#[from] ParseIntError),
|
||||
|
||||
#[error("Can't parse line: {0}")]
|
||||
UnknownLine(String),
|
||||
|
||||
#[error("Did not expect end of Input")]
|
||||
PrematureEndOfInput,
|
||||
#[error("Error while parsing: {0}")]
|
||||
ParsingError(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
impl From<Err<Error<&str>>> for MonkeyError {
|
||||
fn from(value: Err<Error<&str>>) -> Self {
|
||||
MonkeyError::ParsingError(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
enum Operation {
|
||||
Plus(i64),
|
||||
Times(i64),
|
||||
Squared,
|
||||
Times(i64),
|
||||
Plus(i64),
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
pub fn parse(line: &str) -> Operation {
|
||||
match line.split_whitespace().collect::<Vec<_>>()[..] {
|
||||
["*", "old"] => Operation::Squared,
|
||||
["*", value] => Operation::Times(value.parse().unwrap()),
|
||||
["+", value] => Operation::Plus(value.parse().unwrap()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
fn parse(input: &str) -> IResult<&str, Self> {
|
||||
let (input, _) = tag("new = old ")(input)?;
|
||||
alt((
|
||||
value(Operation::Squared, tag("* old")),
|
||||
preceded(char('*'), trim0(i64.map(|a| Operation::Times(a)))),
|
||||
preceded(char('+'), trim0(i64.map(|a| Operation::Plus(a)))),
|
||||
))(input)
|
||||
}
|
||||
|
||||
pub fn calc(&self, old: i64) -> i64 {
|
||||
match self {
|
||||
Operation::Plus(value) => old + *value,
|
||||
Operation::Times(value) => old * value,
|
||||
Operation::Squared => old.pow(2),
|
||||
Operation::Times(value) => old * value,
|
||||
Operation::Plus(value) => old + value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -74,90 +84,96 @@ struct Monkey {
|
|||
bad_monkey: usize,
|
||||
}
|
||||
|
||||
static MONKEY: Lazy<Regex> = Lazy::new(|| Regex::new(r"Monkey (\d+)").unwrap());
|
||||
static STARTING: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"Starting items: (\d+(?:, \d+)*)").unwrap());
|
||||
static OP: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"Operation: new = old ([+*] \d+|\* old)").unwrap());
|
||||
static TEST: Lazy<Regex> = Lazy::new(|| Regex::new(r"Test: divisible by (\d+)").unwrap());
|
||||
static NEXT: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"If (?:true|false): throw to monkey (\d+)").unwrap());
|
||||
|
||||
impl Monkey {
|
||||
fn parse_line<'a>(re: &Regex, line: &'a str) -> Result<&'a str, MonkeyError> {
|
||||
let caps = re
|
||||
.captures(line)
|
||||
.ok_or(MonkeyError::UnknownLine(line.to_owned()))?;
|
||||
Ok(caps.get(1).unwrap().as_str())
|
||||
fn number_parse(input: &str) -> IResult<&str, usize> {
|
||||
let input = ignore(tag("Monkey "))(input)?;
|
||||
let (input, number) = trim0(u32.map(|val| val as usize))(input)?;
|
||||
let input = ignore(char(':'))(input)?;
|
||||
|
||||
Ok((input, number))
|
||||
}
|
||||
|
||||
pub fn parse_one(
|
||||
iterator: &mut dyn Iterator<Item = &str>,
|
||||
) -> Result<Option<Monkey>, MonkeyError> {
|
||||
if let Some(line) = iterator.next() {
|
||||
let number: usize = Monkey::parse_line(&MONKEY, line)?.parse()?;
|
||||
fn starting_items_parse(input: &str) -> IResult<&str, Vec<i64>> {
|
||||
let input = ignore(tag("Starting items:"))(input)?;
|
||||
separated_list1(char(','), trim0(i64))(input)
|
||||
}
|
||||
|
||||
let line = iterator.next().ok_or(MonkeyError::PrematureEndOfInput)?;
|
||||
let start = Monkey::parse_line(&STARTING, line)?;
|
||||
let items = start
|
||||
.split(", ")
|
||||
.map(|item| item.parse::<i64>())
|
||||
.collect::<Result<Vec<i64>, _>>()?;
|
||||
fn operation_parse(input: &str) -> IResult<&str, Operation> {
|
||||
let input = ignore(tag("Operation:"))(input)?;
|
||||
trim0(Operation::parse)(input)
|
||||
}
|
||||
|
||||
let line = iterator.next().ok_or(MonkeyError::PrematureEndOfInput)?;
|
||||
let operation = Operation::parse(Monkey::parse_line(&OP, line)?);
|
||||
fn divisor_parse(input: &str) -> IResult<&str, i64> {
|
||||
let input = ignore(tag("Test: divisible by"))(input)?;
|
||||
trim0(i64)(input)
|
||||
}
|
||||
|
||||
let line = iterator.next().ok_or(MonkeyError::PrematureEndOfInput)?;
|
||||
let divisor: i64 = Monkey::parse_line(&TEST, line)?.parse()?;
|
||||
fn target_parse(want_good: bool) -> impl FnMut(&str) -> IResult<&str, usize> {
|
||||
move |input: &str| {
|
||||
let input = ignore(tuple((
|
||||
tag("If"),
|
||||
trim_left1(verify(true_false, |is_good| *is_good == want_good)),
|
||||
tag(": throw to monkey"),
|
||||
)))(input)?;
|
||||
trim0(u32.map(|val| val as usize))(input)
|
||||
}
|
||||
}
|
||||
|
||||
let line = iterator.next().ok_or(MonkeyError::PrematureEndOfInput)?;
|
||||
let good_monkey: usize = Monkey::parse_line(&NEXT, line)?.parse()?;
|
||||
fn parse(input: &str) -> IResult<&str, Monkey> {
|
||||
let input = empty_lines(input)?;
|
||||
let (input, number) = eol_terminated(trim0(Monkey::number_parse))(input)?;
|
||||
let (input, items) = eol_terminated(trim0(Monkey::starting_items_parse))(input)?;
|
||||
let (input, operation) = eol_terminated(trim0(Monkey::operation_parse))(input)?;
|
||||
let (input, divisor) = eol_terminated(trim0(Monkey::divisor_parse))(input)?;
|
||||
let (input, good_monkey) = eol_terminated(trim0(Monkey::target_parse(true)))(input)?;
|
||||
let (input, bad_monkey) = eol_terminated(trim0(Monkey::target_parse(false)))(input)?;
|
||||
|
||||
let line = iterator.next().ok_or(MonkeyError::PrematureEndOfInput)?;
|
||||
let bad_monkey: usize = Monkey::parse_line(&NEXT, line)?.parse()?;
|
||||
|
||||
iterator.next();
|
||||
|
||||
Ok(Some(Monkey {
|
||||
Ok((
|
||||
input,
|
||||
Monkey {
|
||||
number,
|
||||
items,
|
||||
operation,
|
||||
divisor,
|
||||
good_monkey,
|
||||
bad_monkey,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn process(&self, value: i64) -> i64 {
|
||||
self.operation.calc(value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn check(&self, value: i64) -> bool {
|
||||
value % self.divisor == 0
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn parse_one(lines: &str) -> Result<Monkey, MonkeyError> {
|
||||
Ok(extract_result(Monkey::parse)(lines)?)
|
||||
}
|
||||
}
|
||||
|
||||
struct Troop {
|
||||
monkeys: Vec<Monkey>,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Troop {
|
||||
type Error = MonkeyError;
|
||||
impl FromStr for Troop {
|
||||
type Err = MonkeyError;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
let mut iter = split_lines(value);
|
||||
let mut monkeys = Vec::new();
|
||||
while let Some(monkey) = Monkey::parse_one(&mut iter)? {
|
||||
monkeys.push(monkey);
|
||||
}
|
||||
Ok(Troop { monkeys })
|
||||
fn from_str(lines: &str) -> Result<Self, Self::Err> {
|
||||
Ok(extract_result(Troop::parse)(lines)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Troop {
|
||||
fn parse(input: &str) -> IResult<&str, Troop> {
|
||||
map(many1(Monkey::parse), |monkeys| Troop { monkeys })(input)
|
||||
}
|
||||
|
||||
fn do_play<F>(&self, rounds: usize, alter: F) -> i64
|
||||
where
|
||||
F: Fn(i64) -> i64,
|
||||
|
|
@ -204,7 +220,6 @@ mod test {
|
|||
fn test_parse() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||
let mut lines = split_lines(&lines);
|
||||
let expected = Monkey {
|
||||
number: 0,
|
||||
items: vec![79, 98],
|
||||
|
|
@ -213,7 +228,7 @@ mod test {
|
|||
good_monkey: 2,
|
||||
bad_monkey: 3,
|
||||
};
|
||||
let result = Monkey::parse_one(&mut lines)?.unwrap();
|
||||
let result = Monkey::parse_one(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -224,7 +239,7 @@ mod test {
|
|||
let day = Day {};
|
||||
let lines = read_string(day.get_day_number(), "example01.txt")?;
|
||||
let expected = 4;
|
||||
let result = Troop::try_from(lines.as_str())?;
|
||||
let result: Troop = lines.parse()?;
|
||||
assert_eq!(result.monkeys.len(), expected);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue