diff options
-rw-r--r-- | 2021/rust/Cargo.toml | 2 | ||||
-rw-r--r-- | 2021/rust/day05/Cargo.toml | 10 | ||||
-rw-r--r-- | 2021/rust/day05/src/main.rs | 140 |
3 files changed, 151 insertions, 1 deletions
diff --git a/2021/rust/Cargo.toml b/2021/rust/Cargo.toml index aed5051..3ad3961 100644 --- a/2021/rust/Cargo.toml +++ b/2021/rust/Cargo.toml @@ -4,7 +4,7 @@ members = [ "day02", "day03", "day04", -# "day05", + "day05", # "day06", # "day07", # "day08", diff --git a/2021/rust/day05/Cargo.toml b/2021/rust/day05/Cargo.toml new file mode 100644 index 0000000..6a8f51f --- /dev/null +++ b/2021/rust/day05/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "day05" +version = "0.1.0" +authors = ["cos <cos>"] +edition = "2021" + +[dependencies] +aoc = { path = "../../../common/rust/aoc" } +anyhow = "1.0" +regex = "1.5" diff --git a/2021/rust/day05/src/main.rs b/2021/rust/day05/src/main.rs new file mode 100644 index 0000000..b3bd170 --- /dev/null +++ b/2021/rust/day05/src/main.rs @@ -0,0 +1,140 @@ +use { + anyhow::{ + anyhow, + Context, + Result, + }, + regex::{ + Regex, + }, + std::{ + collections::HashMap, + env::args, + fs::File, + io::{ + BufRead, + BufReader, + }, + iter::repeat, + path::Path, + }, +}; + +#[derive(Debug,Eq,Hash,PartialEq)] +struct Coord { + x: usize, + y: usize, +} + +#[derive(Debug)] +struct Line { + start: Coord, + end: Coord, +} + +impl Line { + fn coords(&self) -> Vec<Coord> { + let (iter_x, iter_y): (Box<dyn Iterator<Item=_>>, Box<dyn Iterator<Item=_>>) = ( + match (self.start.x, self.end.x) { + (start, end) if start < end => Box::new(start..=end), + (start, end) if start > end => Box::new((end..=start).rev()), + (start, _end) /* equals */ => Box::new(repeat(start)), + }, + match (self.start.y, self.end.y) { + (start, end) if start < end => Box::new(start..=end), + (start, end) if start > end => Box::new((end..=start).rev()), + (start, _end) /* equals */ => Box::new(repeat(start)), + }, + ); + iter_x.zip(iter_y).map(|(x, y)| Coord{x, y}).collect() + } +} + +fn read_input<T: AsRef<Path>>(filename: T) -> Result<Vec<Line>> { + let reader = BufReader::new(File::open(filename)?); + let re = Regex::new(r#"(?x) + (?P<x1>[0-9]+), + (?P<y1>[0-9]+)[[:blank:]]*->[[:blank:]]* + (?P<x2>[0-9]+), + (?P<y2>[0-9]+) + "#)?; + + reader.lines().map(|row| { + let string = row?; + let caps = re.captures(&string).ok_or_else(|| anyhow!("Could not parse: {}", string))?; + match (caps.name("x1"), caps.name("y1"), caps.name("x2"), caps.name("y2")) { + (Some(x1), Some(y1), Some(x2), Some(y2)) => { + let x = x1.as_str().parse()?; + let y = y1.as_str().parse()?; + let start = Coord{x, y}; + let x = x2.as_str().parse()?; + let y = y2.as_str().parse()?; + let end = Coord {x, y}; + Ok(Line{start, end}) + }, + _ => Err(anyhow!("Could not parse: {}", string)), + } + }).collect() +} + +fn count_overlaps(points: &HashMap<Coord, usize>) -> Result<usize>{ + let mut overlap = 0; + + let min_x = points.keys().map(|coord| coord.x).min().ok_or(anyhow!("Problem finding min x"))?; + let max_x = points.keys().map(|coord| coord.x).max().ok_or(anyhow!("Problem finding max x"))?; + let min_y = points.keys().map(|coord| coord.y).min().ok_or(anyhow!("Problem finding min y"))?; + let max_y = points.keys().map(|coord| coord.y).max().ok_or(anyhow!("Problem finding max y"))?; + + for y in min_y..=max_y { + for x in min_x..=max_x { + if let Some(v) = points.get(&Coord {x, y}) { + if *v >= 2 { + overlap += 1; + } + } + } + } + + Ok(overlap) +} + +fn part1<'a, I: IntoIterator<Item = &'a Line>>(input: I) -> Result<usize> { + let mut points = HashMap::new(); + + for line in input.into_iter().filter(|l| l.start.x == l.end.x || l.start.y == l.end.y) { + for coord in line.coords() { + let point = points.entry(coord).or_insert(0); + *point += 1; + } + } + + count_overlaps(&points) +} + +fn part2<'a, I: IntoIterator<Item = &'a Line>>(input: I) -> Result<usize> { + let mut points = HashMap::new(); + + for line in input.into_iter() { + for coord in line.coords() { + let point = points.entry(coord).or_insert(0); + *point += 1; + } + } + count_overlaps(&points) +} + +fn main() -> Result<()> { + let ( do_part_1, do_part_2 ) = aoc::do_parts(); + + let filename = args().nth(1).ok_or(anyhow!("Missing input filename"))?; + let input = read_input(filename).context("Could not read input")?; + if do_part_1 { + let solution = part1(&input).context("No solution for part 1")?; + println!("Part1, solution found to be: {}", solution); + } + if do_part_2 { + let solution = part2(&input).context("No solution for part 2")?; + println!("Part2, solution found to be: {}", solution); + } + Ok(()) +} |