diff --git a/Cargo.lock b/Cargo.lock index 387ebd7..bb03321 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,9 +32,9 @@ version = "0.0.0" dependencies = [ "anyhow", "cached", + "derive_more", "glam", "itertools", - "rayon", ] [[package]] @@ -82,31 +82,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - [[package]] name = "darling" version = "0.20.11" @@ -142,6 +117,27 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_more" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + [[package]] name = "either" version = "1.13.0" @@ -233,23 +229,12 @@ dependencies = [ ] [[package]] -name = "rayon" -version = "1.11.0" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", + "semver", ] [[package]] @@ -258,6 +243,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index df45d61..234fed2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,6 @@ edition = "2021" [dependencies] anyhow = "1.0.100" cached = "0.56.0" +derive_more = { version = "2.1.0", features = ["deref"] } glam = "0.30.9" itertools = "0.14.0" -rayon = "1.11.0" - -[profile.release] -debug = true diff --git a/src/bin/09.rs b/src/bin/09.rs index 6c20878..2cdc3cc 100644 --- a/src/bin/09.rs +++ b/src/bin/09.rs @@ -1,14 +1,56 @@ use aoc::*; -use glam::I64Vec2; +use derive_more::Deref; +use glam::{I64Vec2, IVec2, USizeVec2}; use itertools::Itertools; -use rayon::prelude::*; -use std::collections::HashMap; +use std::cmp::Reverse; +use std::collections::{HashMap, HashSet}; +use std::ops::Sub; const INPUT: &str = include_str!("../../input/09"); fn main() { - println!("Part 1: {}", part1(INPUT)); - println!("Part 2: {}", part2(INPUT)); + 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!(); + } } #[test] @@ -27,7 +69,7 @@ fn example() { fn part1(input: &str) -> u64 { Polygon::parse(input) - .points + .0 .into_iter() .tuple_combinations() .map(Rect::from) @@ -40,25 +82,21 @@ fn part2(input: &str) -> u64 { let polygon = Polygon::parse(input); let (polygon, uncompress) = polygon.compress(); polygon - .points .iter() .copied() .tuple_combinations() - .par_bridge() .map(Rect::from) - .filter(|&r| polygon.contains_rect(r)) - .map(|r| r.uncompress(&uncompress).area()) - .max() + .sorted_by_key(|r| { + let area = r.uncompress(&uncompress).area(); + Reverse(area) + }) + .find(|&r| polygon.contains_rect(r)) .unwrap() + .area() } -struct Polygon { - points: Vec, - /// Contains vertical lines sorted by their x value in ascending order. - /// This is important for contains_point, as that assumes the lines are in this order. - /// This dramatically speeds up the raycast in contains_point, as the ray is cast in positive x direction. - cached_vertical_lines: Vec, -} +#[derive(Deref)] +struct Polygon(Vec); /// Uncompress a polygon with this. /// Maps from compressed coords to actual coords. @@ -69,17 +107,12 @@ struct Uncompress { impl Polygon { fn parse(input: &str) -> Self { - let mut polygon = Self { - points: input.lines().map(parse_line).collect(), - cached_vertical_lines: Default::default(), - }; - polygon.cache_vertical_lines(); - polygon + Self(input.lines().map(parse_line).collect()) } fn compress(mut self) -> (Self, Uncompress) { let compress_x: HashMap = self - .points + .0 .iter() .map(|p| p.x) .unique() @@ -88,7 +121,7 @@ impl Polygon { .map(|(i, x)| (x, i as i64)) .collect(); let compress_y: HashMap = self - .points + .0 .iter() .map(|p| p.y) .unique() @@ -96,14 +129,11 @@ impl Polygon { .enumerate() .map(|(i, y)| (y, i as i64)) .collect(); - - self.points = self - .points + self.0 = self + .0 .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(); @@ -124,32 +154,27 @@ impl Polygon { fn contains_point(&self, p: I64Vec2) -> bool { // Interesting values for x let xs = self - .cached_vertical_lines - .iter() - .take_while(|line| line.0.x < p.x) + .vertical_lines() + .into_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 }) - .map(|l| l.0.x) + .flat_map(|l| [l.0.x - 1, l.0.x]) + .filter(|&x| x < p.x) .chain([p.x]) .sorted(); - let line_with = |p: I64Vec2| { - self.cached_vertical_lines - .iter() - .take_while(|line| line.0.x <= p.x) - .find(|line| line.contains(p)) - .copied() - }; + let lines = self.vertical_lines(); + let line = |p| lines.iter().find(|line| line.contains(p)).copied(); let mut winding = 0; let mut on_line = false; for x in xs { let check = I64Vec2::new(x, p.y); - if let Some(line) = line_with(check) { + if let Some(line) = line(check) { winding = line.winding(); on_line = true; } else { @@ -160,25 +185,38 @@ impl Polygon { winding < 0 || on_line } - fn cache_vertical_lines(&mut self) { - self.cached_vertical_lines = self - .points - .iter() - .chain(self.points.first()) // close the loop + fn vertical_lines(&self) -> Vec { + self.iter() + .chain(self.first()) // close the loop .copied() .tuple_windows() .map(|(a, b)| Line(a, b)) .filter(|l| l.is_vertical()) - .sorted_by_key(|line| line.0.x) - .collect(); + .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 + // } } #[derive(Clone, Copy, Debug)] struct Line(I64Vec2, I64Vec2); impl Line { - /// true if p on line + /// true if p on Line(a, b) fn contains(self, p: I64Vec2) -> bool { if self.is_vertical() { let in_plane = p.x == self.0.x; @@ -193,6 +231,10 @@ 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 { @@ -218,9 +260,12 @@ 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) @@ -237,6 +282,27 @@ 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); @@ -258,10 +324,6 @@ 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())