Cleanup for 2022 days 11 through 21: Turned into a lib and introduced parse errors
This commit is contained in:
parent
c7852c9791
commit
fcb2fed515
26 changed files with 1095 additions and 879 deletions
|
@ -1,6 +1,29 @@
|
|||
use std::{fs, collections::{HashSet, HashMap}};
|
||||
use core::fmt::Display;
|
||||
use std::num::ParseIntError;
|
||||
use std::collections::{BTreeSet, BTreeMap};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ParseError<'a> {
|
||||
ParseIntError(std::num::ParseIntError),
|
||||
LineMalformed(&'a str),
|
||||
}
|
||||
|
||||
impl From<ParseIntError> for ParseError<'_> {
|
||||
fn from(value: ParseIntError) -> Self {
|
||||
Self::ParseIntError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ParseError<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::LineMalformed(v) => write!(f, "Line is malformed: {v}"),
|
||||
Self::ParseIntError(e) => write!(f, "Unable to parse into integer: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct Position {
|
||||
x: isize,
|
||||
y: isize,
|
||||
|
@ -18,11 +41,13 @@ struct Sensor {
|
|||
beacon_distance: isize,
|
||||
}
|
||||
|
||||
impl Sensor {
|
||||
fn from(reading: &str) -> Self {
|
||||
let components = reading.split(' ').collect::<Vec<&str>>();
|
||||
impl <'a> TryFrom<&'a str> for Sensor {
|
||||
type Error = ParseError<'a>;
|
||||
|
||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||
let components = value.split(' ').collect::<Vec<&str>>();
|
||||
if components.len() != 10 {
|
||||
panic!("{components:?} does not have 10 items.");
|
||||
return Err(Self::Error::LineMalformed(value));
|
||||
}
|
||||
|
||||
let sensor_x_str = &components[2][2..];
|
||||
|
@ -30,9 +55,9 @@ impl Sensor {
|
|||
let beacon_x_str = &components[8][2..];
|
||||
let beacon_y_str = &components[9][2..];
|
||||
|
||||
let sensor_x = sensor_x_str[0..sensor_x_str.len()-1].parse::<isize>().unwrap();
|
||||
let sensor_y = sensor_y_str[0..sensor_y_str.len()-1].parse::<isize>().unwrap();
|
||||
let beacon_x = beacon_x_str[0..beacon_x_str.len()-1].parse::<isize>().unwrap();
|
||||
let sensor_x = sensor_x_str[0..sensor_x_str.len()-1].parse::<isize>()?;
|
||||
let sensor_y = sensor_y_str[0..sensor_y_str.len()-1].parse::<isize>()?;
|
||||
let beacon_x = beacon_x_str[0..beacon_x_str.len()-1].parse::<isize>()?;
|
||||
let beacon_y = beacon_y_str[0..].parse::<isize>().unwrap();
|
||||
|
||||
let position = Position {
|
||||
|
@ -44,40 +69,42 @@ impl Sensor {
|
|||
y: beacon_y,
|
||||
};
|
||||
|
||||
Self {
|
||||
position: position.clone(),
|
||||
Ok(Self {
|
||||
position,
|
||||
beacon_distance: position.distance_to(&beacon_position),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn beacon_from(reading: &str) -> Position {
|
||||
impl Sensor {
|
||||
fn beacon_from(reading: &str) -> Result<Position, ParseError> {
|
||||
let components = reading.split(' ').collect::<Vec<&str>>();
|
||||
if components.len() != 10 {
|
||||
panic!("{components:?} does not have 10 items.");
|
||||
return Err(ParseError::LineMalformed(reading));
|
||||
}
|
||||
|
||||
let beacon_x_str = &components[8][2..];
|
||||
let beacon_y_str = &components[9][2..];
|
||||
|
||||
let beacon_x = beacon_x_str[0..beacon_x_str.len()-1].parse::<isize>().unwrap();
|
||||
let beacon_y = beacon_y_str[0..].parse::<isize>().unwrap();
|
||||
let beacon_x = beacon_x_str[0..beacon_x_str.len()-1].parse::<isize>()?;
|
||||
let beacon_y = beacon_y_str[0..].parse::<isize>()?;
|
||||
|
||||
Position {
|
||||
Ok(Position {
|
||||
x: beacon_x,
|
||||
y: beacon_y,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fn at_row(&self, row: isize) -> HashSet<isize> {
|
||||
fn at_row(&self, row: isize) -> BTreeSet<isize> {
|
||||
let slice_depth = self.beacon_distance - (row-self.position.y).abs();
|
||||
match slice_depth {
|
||||
nope if nope <= 0 => HashSet::new(),
|
||||
nope if nope <= 0 => BTreeSet::new(),
|
||||
_ => (self.position.x-slice_depth..=self.position.x+slice_depth).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn first_non_reachables(&self, unreachables: &mut HashMap<Position, bool>, min: isize, max: isize) {
|
||||
fn first_non_reachables(&self, unreachables: &mut BTreeMap<Position, bool>, min: isize, max: isize) {
|
||||
// top right
|
||||
for i in 0..=self.beacon_distance {
|
||||
let x = self.position.x+i;
|
||||
|
@ -117,12 +144,7 @@ impl Sensor {
|
|||
}
|
||||
|
||||
|
||||
fn read_file(path: &str) -> String {
|
||||
fs::read_to_string(path)
|
||||
.expect("File not Found")
|
||||
}
|
||||
|
||||
fn beacon_free_positions(row: isize, sensors: &[Sensor], beacons: &HashSet<Position>) -> usize {
|
||||
fn beacon_free_positions(row: isize, sensors: &[Sensor], beacons: &BTreeSet<Position>) -> usize {
|
||||
sensors.iter()
|
||||
.map(|s| s.at_row(row))
|
||||
.reduce(|a, b| a.union(&b).cloned().collect())
|
||||
|
@ -139,45 +161,51 @@ fn is_reachable_by(position: &Position, sensors: &[Sensor]) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
fn get_non_reachable(sensors: &[Sensor], beacons: &HashSet<Position>, max: isize) -> isize {
|
||||
let mut first_non_reachables = HashMap::new();
|
||||
fn get_non_reachable(sensors: &[Sensor], beacons: &BTreeSet<Position>, max: isize) -> isize {
|
||||
let mut first_non_reachables = BTreeMap::new();
|
||||
sensors.iter()
|
||||
.for_each(|s| s.first_non_reachables(&mut first_non_reachables, 0, max));
|
||||
|
||||
if let Some((&pos, _)) = &first_non_reachables.iter()
|
||||
.find(|(pos, &repeat)| repeat && !beacons.contains(pos) && !is_reachable_by(pos, sensors)) {
|
||||
pos.x * 4_000_000 + pos.y
|
||||
} else {
|
||||
0
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(input: &str) -> Result<(usize, isize), ParseError> {
|
||||
let sensors = input.lines().map(Sensor::try_from).collect::<Result<Vec<_>, _>>()?;
|
||||
let beacons = input.lines().map(Sensor::beacon_from).collect::<Result<BTreeSet<_>, _>>()?;
|
||||
|
||||
let first = beacon_free_positions(2_000_000, &sensors, &beacons);
|
||||
let second = get_non_reachable(&sensors, &beacons, 4_000_000);
|
||||
Ok((first, second))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
fn read_file(name: &str) -> String {
|
||||
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_input() {
|
||||
let readings = read_file("tests/sample_input");
|
||||
let sensors = readings.lines().map(Sensor::try_from).collect::<Result<Vec<_>, _>>().unwrap();
|
||||
let beacons = readings.lines().map(Sensor::beacon_from).collect::<Result<BTreeSet<_>, _>>().unwrap();
|
||||
|
||||
assert_eq!(beacon_free_positions(10, &sensors, &beacons), 26);
|
||||
assert_eq!(get_non_reachable(&sensors, &beacons, 20), 56000011);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn challenge_input() {
|
||||
let readings = read_file("tests/challenge_input");
|
||||
|
||||
assert_eq!(run(&readings), Ok((5367037, 11914583249288)));
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let readings = read_file("input");
|
||||
|
||||
let sensors = readings.lines().map(Sensor::from).collect::<Vec<_>>();
|
||||
let beacons = readings.lines().map(Sensor::beacon_from).collect::<HashSet<_>>();
|
||||
|
||||
println!("Not in Line 2_000_000: {}", beacon_free_positions(2_000_000, &sensors, &beacons));
|
||||
println!("Non-Reachable Position found with frequency {}.", get_non_reachable(&sensors, &beacons, 4_000_000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_input() {
|
||||
let readings = read_file("tests/sample_input");
|
||||
let sensors = readings.lines().map(Sensor::from).collect::<Vec<_>>();
|
||||
let beacons = readings.lines().map(Sensor::beacon_from).collect::<HashSet<_>>();
|
||||
|
||||
assert_eq!(beacon_free_positions(10, &sensors, &beacons), 26);
|
||||
assert_eq!(get_non_reachable(&sensors, &beacons, 20), 56000011);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn challenge_input() {
|
||||
let readings = read_file("tests/input");
|
||||
let sensors = readings.lines().map(Sensor::from).collect::<Vec<_>>();
|
||||
let beacons = readings.lines().map(Sensor::beacon_from).collect::<HashSet<_>>();
|
||||
|
||||
assert_eq!(beacon_free_positions(2_000_000, &sensors, &beacons), 5367037);
|
||||
assert_eq!(get_non_reachable(&sensors, &beacons, 4_000_000), 11914583249288);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue