switch to nom
This commit is contained in:
parent
a5f19ecae1
commit
b00835c25e
7 changed files with 288 additions and 181 deletions
|
|
@ -1,37 +1,44 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use crate::common::{file::split_lines, pos::Pos};
|
||||
use crate::common::{
|
||||
parser::{eol_terminated, extract_result, ignore, trim0},
|
||||
pos::Pos,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::{collections::HashSet, num::ParseIntError};
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete::{char, i64},
|
||||
error::Error,
|
||||
multi::many0,
|
||||
sequence::{separated_pair, tuple},
|
||||
Err, IResult,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 15;
|
||||
|
||||
pub struct Day;
|
||||
|
||||
fn parse_row(input: &str) -> IResult<&str, i64> {
|
||||
eol_terminated(i64)(input)
|
||||
}
|
||||
|
||||
impl DayTrait for Day {
|
||||
fn get_day_number(&self) -> usize {
|
||||
DAY_NUMBER
|
||||
}
|
||||
|
||||
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||
let lines = split_lines(lines).collect_vec();
|
||||
if lines.len() < 2 {
|
||||
Err(SensorError::ToFewLines)?;
|
||||
}
|
||||
let row = lines[0].parse()?;
|
||||
let (sensors, beacons) = Day::parse_all(&lines[1..])?;
|
||||
let (lines, row) = parse_row(lines).map_err(|_| SensorError::RowLineNotFound)?;
|
||||
let (sensors, beacons) = Day::parse_all(lines)?;
|
||||
|
||||
let result = Day::count_coverage_at(&sensors, &beacons, row);
|
||||
Ok(ResultType::Integer(result))
|
||||
}
|
||||
|
||||
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
|
||||
let lines = split_lines(lines).collect_vec();
|
||||
if lines.len() < 2 {
|
||||
Err(SensorError::ToFewLines)?;
|
||||
}
|
||||
let (sensors, _) = Day::parse_all(&lines[1..])?;
|
||||
let (lines, _) = parse_row(lines).map_err(|_| SensorError::RowLineNotFound)?;
|
||||
let (sensors, _) = Day::parse_all(lines)?;
|
||||
let mid_lines = Day::dividing_lines(&sensors);
|
||||
let points = mid_lines
|
||||
.iter()
|
||||
|
|
@ -51,11 +58,12 @@ impl DayTrait for Day {
|
|||
}
|
||||
|
||||
impl Day {
|
||||
fn parse_all(lines: &[&str]) -> Result<(HashSet<Sensor>, HashSet<Pos<i64>>), SensorError> {
|
||||
fn parse_all(lines: &str) -> Result<(HashSet<Sensor>, HashSet<Pos<i64>>), SensorError> {
|
||||
let data = extract_result(many0(eol_terminated(Sensor::parse)))(lines)?;
|
||||
|
||||
let mut sensors = HashSet::new();
|
||||
let mut beacons = HashSet::new();
|
||||
for line in lines {
|
||||
let (sensor, beacon) = Sensor::parse(line)?;
|
||||
for (sensor, beacon) in data {
|
||||
sensors.insert(sensor);
|
||||
beacons.insert(beacon);
|
||||
}
|
||||
|
|
@ -91,17 +99,20 @@ impl Day {
|
|||
|
||||
#[derive(Debug, Error)]
|
||||
enum SensorError {
|
||||
#[error("Not an Integer")]
|
||||
NotAnInt(#[from] ParseIntError),
|
||||
|
||||
#[error("Unknown line: {0}")]
|
||||
UnknownLine(String),
|
||||
#[error("Error Parsing Input: {0}")]
|
||||
ParsingError(String),
|
||||
|
||||
#[error("Did not find exactly one point for the sensor: {0}")]
|
||||
NotExactlyOneResult(usize),
|
||||
|
||||
#[error("Too few lines in input.txt")]
|
||||
ToFewLines,
|
||||
#[error("Row line not found at start of Input")]
|
||||
RowLineNotFound,
|
||||
}
|
||||
|
||||
impl From<Err<Error<&str>>> for SensorError {
|
||||
fn from(error: Err<Error<&str>>) -> Self {
|
||||
SensorError::ParsingError(error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -220,28 +231,30 @@ struct Sensor {
|
|||
radius: i64,
|
||||
}
|
||||
|
||||
static SENSOR: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"x=(-?\d+), y=(-?\d+).*x=(-?\d+), y=(-?\d+)").unwrap());
|
||||
|
||||
impl Sensor {
|
||||
pub fn parse(line: &str) -> Result<(Sensor, Pos<i64>), SensorError> {
|
||||
let caps = SENSOR
|
||||
.captures(line)
|
||||
.ok_or(SensorError::UnknownLine(line.to_owned()))?;
|
||||
let sensor_x = caps.get(1).unwrap().as_str().parse()?;
|
||||
let sensor_y = caps.get(2).unwrap().as_str().parse()?;
|
||||
let beacon_x = caps.get(3).unwrap().as_str().parse()?;
|
||||
let beacon_y = caps.get(4).unwrap().as_str().parse()?;
|
||||
let sensor = Pos::new(sensor_x, sensor_y);
|
||||
let beacon = Pos::new(beacon_x, beacon_y);
|
||||
let radius = sensor.taxicab(&beacon);
|
||||
Ok((
|
||||
Sensor {
|
||||
pos: sensor,
|
||||
radius,
|
||||
},
|
||||
beacon,
|
||||
))
|
||||
fn component<'a>(name: &'a str) -> impl FnMut(&'a str) -> IResult<&'a str, i64> {
|
||||
move |input: &str| {
|
||||
let input = ignore(tuple((tag(name), char('='))))(input)?;
|
||||
trim0(i64)(input)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_pos(input: &str) -> IResult<&str, Pos<i64>> {
|
||||
let (input, (x, y)) = separated_pair(
|
||||
Sensor::component("x"),
|
||||
trim0(char(',')),
|
||||
Sensor::component("y"),
|
||||
)(input)?;
|
||||
Ok((input, Pos::new(x, y)))
|
||||
}
|
||||
|
||||
pub fn parse(input: &str) -> IResult<&str, (Sensor, Pos<i64>)> {
|
||||
let input = ignore(tag("Sensor at"))(input)?;
|
||||
let (input, pos) = trim0(Sensor::parse_pos)(input)?;
|
||||
let input = ignore(tag(": closest beacon is at"))(input)?;
|
||||
let (input, beacon) = trim0(Sensor::parse_pos)(input)?;
|
||||
let radius = pos.taxicab(&beacon);
|
||||
Ok((input, (Sensor { pos, radius }, beacon)))
|
||||
}
|
||||
|
||||
pub fn range_at(&self, y: i64) -> Option<Range> {
|
||||
|
|
@ -279,7 +292,7 @@ mod test {
|
|||
},
|
||||
Pos::new(-2, 15),
|
||||
);
|
||||
let result = Sensor::parse(input)?;
|
||||
let result = extract_result(Sensor::parse)(input)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue