1
0
Fork 0

Compare commits

..

2 commits

Author SHA1 Message Date
257c7e5cb6
Optimize 09 2025-12-09 18:58:18 +01:00
c6d69e2fc2
Solve 09 2025-12-09 18:14:38 +01:00
3 changed files with 101 additions and 151 deletions

73
Cargo.lock generated
View file

@ -32,9 +32,9 @@ version = "0.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cached", "cached",
"derive_more",
"glam", "glam",
"itertools", "itertools",
"rayon",
] ]
[[package]] [[package]]
@ -82,6 +82,31 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 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]] [[package]]
name = "darling" name = "darling"
version = "0.20.11" version = "0.20.11"
@ -117,27 +142,6 @@ dependencies = [
"syn", "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]] [[package]]
name = "either" name = "either"
version = "1.13.0" version = "1.13.0"
@ -229,12 +233,23 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rustc_version" name = "rayon"
version = "0.4.1" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
dependencies = [ dependencies = [
"semver", "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",
] ]
[[package]] [[package]]
@ -243,12 +258,6 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"

View file

@ -6,6 +6,9 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.100" anyhow = "1.0.100"
cached = "0.56.0" cached = "0.56.0"
derive_more = { version = "2.1.0", features = ["deref"] }
glam = "0.30.9" glam = "0.30.9"
itertools = "0.14.0" itertools = "0.14.0"
rayon = "1.11.0"
[profile.release]
debug = true

View file

@ -1,56 +1,14 @@
use aoc::*; use aoc::*;
use derive_more::Deref; use glam::I64Vec2;
use glam::{I64Vec2, IVec2, USizeVec2};
use itertools::Itertools; use itertools::Itertools;
use std::cmp::Reverse; use rayon::prelude::*;
use std::collections::{HashMap, HashSet}; use std::collections::HashMap;
use std::ops::Sub;
const INPUT: &str = include_str!("../../input/09"); const INPUT: &str = include_str!("../../input/09");
fn main() { fn main() {
let polygon = Polygon::parse(include_str!("../../input/09-test")); println!("Part 1: {}", part1(INPUT));
// let polygon = Polygon::parse("2,2\n2,5\n5,5\n5,2\n"); println!("Part 2: {}", part2(INPUT));
// 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] #[test]
@ -69,7 +27,7 @@ fn example() {
fn part1(input: &str) -> u64 { fn part1(input: &str) -> u64 {
Polygon::parse(input) Polygon::parse(input)
.0 .points
.into_iter() .into_iter()
.tuple_combinations() .tuple_combinations()
.map(Rect::from) .map(Rect::from)
@ -82,21 +40,25 @@ fn part2(input: &str) -> u64 {
let polygon = Polygon::parse(input); let polygon = Polygon::parse(input);
let (polygon, uncompress) = polygon.compress(); let (polygon, uncompress) = polygon.compress();
polygon polygon
.points
.iter() .iter()
.copied() .copied()
.tuple_combinations() .tuple_combinations()
.par_bridge()
.map(Rect::from) .map(Rect::from)
.sorted_by_key(|r| { .filter(|&r| polygon.contains_rect(r))
let area = r.uncompress(&uncompress).area(); .map(|r| r.uncompress(&uncompress).area())
Reverse(area) .max()
})
.find(|&r| polygon.contains_rect(r))
.unwrap() .unwrap()
.area()
} }
#[derive(Deref)] struct Polygon {
struct Polygon(Vec<I64Vec2>); points: Vec<I64Vec2>,
/// 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<Line>,
}
/// Uncompress a polygon with this. /// Uncompress a polygon with this.
/// Maps from compressed coords to actual coords. /// Maps from compressed coords to actual coords.
@ -107,12 +69,17 @@ struct Uncompress {
impl Polygon { impl Polygon {
fn parse(input: &str) -> Self { 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: Default::default(),
};
polygon.cache_vertical_lines();
polygon
} }
fn compress(mut self) -> (Self, Uncompress) { fn compress(mut self) -> (Self, Uncompress) {
let compress_x: HashMap<i64, i64> = self let compress_x: HashMap<i64, i64> = self
.0 .points
.iter() .iter()
.map(|p| p.x) .map(|p| p.x)
.unique() .unique()
@ -121,7 +88,7 @@ impl Polygon {
.map(|(i, x)| (x, i as i64)) .map(|(i, x)| (x, i as i64))
.collect(); .collect();
let compress_y: HashMap<i64, i64> = self let compress_y: HashMap<i64, i64> = self
.0 .points
.iter() .iter()
.map(|p| p.y) .map(|p| p.y)
.unique() .unique()
@ -129,11 +96,14 @@ impl Polygon {
.enumerate() .enumerate()
.map(|(i, y)| (y, i as i64)) .map(|(i, y)| (y, i as i64))
.collect(); .collect();
self.0 = self
.0 self.points = self
.points
.into_iter() .into_iter()
.map(|p| I64Vec2::new(compress_x[&p.x], compress_y[&p.y])) .map(|p| I64Vec2::new(compress_x[&p.x], compress_y[&p.y]))
.collect(); .collect();
self.cache_vertical_lines();
let uncompress_x = compress_x.into_iter().map(|(x, i)| (i, x)).collect(); 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(); let uncompress_y = compress_y.into_iter().map(|(y, i)| (i, y)).collect();
@ -154,27 +124,32 @@ impl Polygon {
fn contains_point(&self, p: I64Vec2) -> bool { fn contains_point(&self, p: I64Vec2) -> bool {
// Interesting values for x // Interesting values for x
let xs = self let xs = self
.vertical_lines() .cached_vertical_lines
.into_iter() .iter()
.take_while(|line| line.0.x < p.x)
.filter(|l| { .filter(|l| {
let ymin = l.0.y.min(l.1.y); let ymin = l.0.y.min(l.1.y);
let ymax = l.0.y.max(l.1.y); let ymax = l.0.y.max(l.1.y);
ymin <= p.y && p.y <= ymax ymin <= p.y && p.y <= ymax
}) })
.flat_map(|l| [l.0.x - 1, l.0.x]) .map(|l| l.0.x)
.filter(|&x| x < p.x)
.chain([p.x]) .chain([p.x])
.sorted(); .sorted();
let lines = self.vertical_lines(); let line_with = |p: I64Vec2| {
let line = |p| lines.iter().find(|line| line.contains(p)).copied(); self.cached_vertical_lines
.iter()
.take_while(|line| line.0.x <= p.x)
.find(|line| line.contains(p))
.copied()
};
let mut winding = 0; let mut winding = 0;
let mut on_line = false; let mut on_line = false;
for x in xs { for x in xs {
let check = I64Vec2::new(x, p.y); let check = I64Vec2::new(x, p.y);
if let Some(line) = line(check) { if let Some(line) = line_with(check) {
winding = line.winding(); winding = line.winding();
on_line = true; on_line = true;
} else { } else {
@ -185,38 +160,25 @@ impl Polygon {
winding < 0 || on_line winding < 0 || on_line
} }
fn vertical_lines(&self) -> Vec<Line> { fn cache_vertical_lines(&mut self) {
self.iter() self.cached_vertical_lines = self
.chain(self.first()) // close the loop .points
.iter()
.chain(self.points.first()) // close the loop
.copied() .copied()
.tuple_windows() .tuple_windows()
.map(|(a, b)| Line(a, b)) .map(|(a, b)| Line(a, b))
.filter(|l| l.is_vertical()) .filter(|l| l.is_vertical())
.collect() .sorted_by_key(|line| line.0.x)
.collect();
} }
// fn area(&self) -> HashSet<I64Vec2> {
// 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)] #[derive(Clone, Copy, Debug)]
struct Line(I64Vec2, I64Vec2); struct Line(I64Vec2, I64Vec2);
impl Line { impl Line {
/// true if p on Line(a, b) /// true if p on line
fn contains(self, p: I64Vec2) -> bool { fn contains(self, p: I64Vec2) -> bool {
if self.is_vertical() { if self.is_vertical() {
let in_plane = p.x == self.0.x; let in_plane = p.x == self.0.x;
@ -231,10 +193,6 @@ impl Line {
let in_bounds = xmin <= p.x && p.x <= xmax; let in_bounds = xmin <= p.x && p.x <= xmax;
in_bounds && in_plane 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 { fn is_vertical(self) -> bool {
@ -260,12 +218,9 @@ impl Line {
} }
} }
fn uncompress_i64vec2(v: I64Vec2, uncompress: &Uncompress) -> I64Vec2 {
I64Vec2::new(uncompress.x[&v.x], uncompress.y[&v.y])
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct Rect(I64Vec2, I64Vec2); struct Rect(I64Vec2, I64Vec2);
impl Rect { impl Rect {
fn area(self) -> u64 { fn area(self) -> u64 {
(self.0.x.abs_diff(self.1.x) + 1) * (self.0.y.abs_diff(self.1.y) + 1) (self.0.x.abs_diff(self.1.x) + 1) * (self.0.y.abs_diff(self.1.y) + 1)
@ -282,27 +237,6 @@ impl Rect {
Self(a, b) 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<I64Vec2> {
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] { fn lines(self) -> [Line; 4] {
let x1 = self.0.x.min(self.1.x); let x1 = self.0.x.min(self.1.x);
let x2 = self.0.x.max(self.1.x); let x2 = self.0.x.max(self.1.x);
@ -324,6 +258,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 { fn parse_line(line: &str) -> I64Vec2 {
let (x, y) = line.split_once(',').unwrap(); let (x, y) = line.split_once(',').unwrap();
I64Vec2::new(x.parse().unwrap(), y.parse().unwrap()) I64Vec2::new(x.parse().unwrap(), y.parse().unwrap())