diff --git a/Cargo.toml b/Cargo.toml index 234fed2..63cbbb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,6 @@ cached = "0.56.0" derive_more = { version = "2.1.0", features = ["deref"] } glam = "0.30.9" itertools = "0.14.0" + +[profile.release] +debug = true diff --git a/src/bin/09.rs b/src/bin/09.rs index 2cdc3cc..4de6d4c 100644 --- a/src/bin/09.rs +++ b/src/bin/09.rs @@ -1,56 +1,15 @@ use aoc::*; use derive_more::Deref; -use glam::{I64Vec2, IVec2, USizeVec2}; +use glam::I64Vec2; use itertools::Itertools; use std::cmp::Reverse; -use std::collections::{HashMap, HashSet}; -use std::ops::Sub; +use std::collections::HashMap; const INPUT: &str = include_str!("../../input/09"); fn main() { - let polygon = Polygon::parse(include_str!("../../input/09-test")); - // let polygon = Polygon::parse("2,2\n2,5\n5,5\n5,2\n"); - // let polygon = Polygon::parse("0,0\n2,0\n2,2\n0,2\n"); - // for line in polygon.vertical_lines() { - // println!("{line:?} {}", line.winding()); - // } - let (polygon, uncompress) = polygon.compress(); - print_corners(&polygon); - println!("==="); - print_area(&polygon); - // println!("Part 1: {}", part1(INPUT)); - // let p2 = part2(INPUT); - // println!("Part 2: {}", p2); - // assert!(p2 < 4583207265); -} - -fn print_corners(p: &Polygon) { - for y in 0..=10 { - for x in 0..=20 { - let draw = p.contains(&I64Vec2::new(x, y)); - if draw { - print!("#"); - } else { - print!("."); - } - } - println!(); - } -} - -fn print_area(p: &Polygon) { - for y in 0..=10 { - for x in 0..=20 { - let draw = p.contains_point(I64Vec2::new(x, y)); - if draw { - print!("#"); - } else { - print!("."); - } - } - println!(); - } + println!("Part 1: {}", part1(INPUT)); + println!("Part 2: {}", part2(INPUT)); } #[test] @@ -69,7 +28,7 @@ fn example() { fn part1(input: &str) -> u64 { Polygon::parse(input) - .0 + .points .into_iter() .tuple_combinations() .map(Rect::from) @@ -82,6 +41,7 @@ fn part2(input: &str) -> u64 { let polygon = Polygon::parse(input); let (polygon, uncompress) = polygon.compress(); polygon + .points .iter() .copied() .tuple_combinations() @@ -92,11 +52,14 @@ fn part2(input: &str) -> u64 { }) .find(|&r| polygon.contains_rect(r)) .unwrap() + .uncompress(&uncompress) .area() } -#[derive(Deref)] -struct Polygon(Vec); +struct Polygon { + points: Vec, + cached_vertical_lines: Vec, +} /// Uncompress a polygon with this. /// Maps from compressed coords to actual coords. @@ -107,12 +70,17 @@ struct Uncompress { impl Polygon { fn parse(input: &str) -> Self { - Self(input.lines().map(parse_line).collect()) + let mut polygon = Self { + points: input.lines().map(parse_line).collect(), + cached_vertical_lines: vec![], + }; + polygon.cache_vertical_lines(); + polygon } fn compress(mut self) -> (Self, Uncompress) { let compress_x: HashMap = self - .0 + .points .iter() .map(|p| p.x) .unique() @@ -121,7 +89,7 @@ impl Polygon { .map(|(i, x)| (x, i as i64)) .collect(); let compress_y: HashMap = self - .0 + .points .iter() .map(|p| p.y) .unique() @@ -129,11 +97,14 @@ impl Polygon { .enumerate() .map(|(i, y)| (y, i as i64)) .collect(); - self.0 = self - .0 + + self.points = self + .points .into_iter() .map(|p| I64Vec2::new(compress_x[&p.x], compress_y[&p.y])) .collect(); + self.cache_vertical_lines(); + let uncompress_x = compress_x.into_iter().map(|(x, i)| (i, x)).collect(); let uncompress_y = compress_y.into_iter().map(|(y, i)| (i, y)).collect(); @@ -154,20 +125,24 @@ impl Polygon { fn contains_point(&self, p: I64Vec2) -> bool { // Interesting values for x let xs = self - .vertical_lines() - .into_iter() + .cached_vertical_lines + .iter() .filter(|l| { let ymin = l.0.y.min(l.1.y); let ymax = l.0.y.max(l.1.y); ymin <= p.y && p.y <= ymax }) - .flat_map(|l| [l.0.x - 1, l.0.x]) + .flat_map(|l| [l.0.x]) .filter(|&x| x < p.x) .chain([p.x]) .sorted(); - let lines = self.vertical_lines(); - let line = |p| lines.iter().find(|line| line.contains(p)).copied(); + let line = |p| { + self.cached_vertical_lines + .iter() + .find(|line| line.contains(p)) + .copied() + }; let mut winding = 0; let mut on_line = false; @@ -186,8 +161,9 @@ impl Polygon { } fn vertical_lines(&self) -> Vec { - self.iter() - .chain(self.first()) // close the loop + self.points + .iter() + .chain(self.points.first()) // close the loop .copied() .tuple_windows() .map(|(a, b)| Line(a, b)) @@ -195,28 +171,16 @@ impl Polygon { .collect() } - // fn area(&self) -> HashSet { - // let (xmin, xmax) = self.0.iter().map(|p| p.x).minmax().into_option().unwrap(); - // let (ymin, ymax) = self.0.iter().map(|p| p.y).minmax().into_option().unwrap(); - // - // let mut result = HashSet::new(); - // for x in xmin..=xmax { - // for y in ymin..=ymax { - // let p = I64Vec2::new(x, y); - // if self.contains_point(p) { - // result.insert(p); - // } - // } - // } - // result - // } + fn cache_vertical_lines(&mut self) { + self.cached_vertical_lines = self.vertical_lines(); + } } #[derive(Clone, Copy, Debug)] struct Line(I64Vec2, I64Vec2); impl Line { - /// true if p on Line(a, b) + /// true if p on line fn contains(self, p: I64Vec2) -> bool { if self.is_vertical() { let in_plane = p.x == self.0.x; @@ -231,10 +195,6 @@ impl Line { let in_bounds = xmin <= p.x && p.x <= xmax; in_bounds && in_plane } - // let a = self.0.sub(p).length_squared(); - // let b = self.1.sub(p).length_squared(); - // let c = self.0.sub(self.1).length_squared(); - // a + b == c } fn is_vertical(self) -> bool { @@ -260,12 +220,9 @@ impl Line { } } -fn uncompress_i64vec2(v: I64Vec2, uncompress: &Uncompress) -> I64Vec2 { - I64Vec2::new(uncompress.x[&v.x], uncompress.y[&v.y]) -} - #[derive(Copy, Clone)] struct Rect(I64Vec2, I64Vec2); + impl Rect { fn area(self) -> u64 { (self.0.x.abs_diff(self.1.x) + 1) * (self.0.y.abs_diff(self.1.y) + 1) @@ -282,27 +239,6 @@ impl Rect { Self(a, b) } - fn corners(self) -> [I64Vec2; 4] { - [ - I64Vec2::new(self.0.x, self.0.y), - I64Vec2::new(self.1.x, self.0.y), - I64Vec2::new(self.0.x, self.1.y), - I64Vec2::new(self.1.x, self.1.y), - ] - } - - fn all_points(self) -> Vec { - let mut result = Vec::new(); - - for x in self.0.x.min(self.1.x)..=self.0.x.max(self.1.x) { - for y in self.0.y.min(self.1.y)..=self.0.y.max(self.1.y) { - result.push(I64Vec2::new(x, y)); - } - } - - result - } - fn lines(self) -> [Line; 4] { let x1 = self.0.x.min(self.1.x); let x2 = self.0.x.max(self.1.x); @@ -324,6 +260,10 @@ impl Rect { } } +fn uncompress_i64vec2(v: I64Vec2, uncompress: &Uncompress) -> I64Vec2 { + I64Vec2::new(uncompress.x[&v.x], uncompress.y[&v.y]) +} + fn parse_line(line: &str) -> I64Vec2 { let (x, y) = line.split_once(',').unwrap(); I64Vec2::new(x.parse().unwrap(), y.parse().unwrap())