summaryrefslogtreecommitdiff
path: root/2021/rust/day05/src
diff options
context:
space:
mode:
Diffstat (limited to '2021/rust/day05/src')
-rw-r--r--2021/rust/day05/src/main.rs140
1 files changed, 140 insertions, 0 deletions
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(())
+}