Compare commits

...

2 commits

Author SHA1 Message Date
Quentin Boyer
a8ee7f78f6 Cache the edge calculations 2025-12-10 01:33:07 +01:00
Quentin Boyer
8662ab104f Cache the polygon containement 2025-12-10 01:20:35 +01:00

View file

@ -1,4 +1,9 @@
use std::{collections::HashSet, ops::RangeInclusive, time::Instant};
use std::{
collections::{HashMap, HashSet},
hash::Hash,
ops::RangeInclusive,
time::Instant,
};
use aoc_2025::{load, print_res};
use bstr::BString;
@ -179,7 +184,28 @@ impl Polygon {
}
}
fn horizontal_edges_contained(poly: &Polygon, a: Point2, b: Point2) -> bool {
struct InsertGuard<'a, K, V>(K, V, &'a mut HashMap<K, V>)
where
K: Hash + Eq + Copy,
V: Copy;
impl<'a, K, V> Drop for InsertGuard<'a, K, V>
where
K: Hash + Eq + Copy,
V: Copy,
{
fn drop(&mut self) {
self.2.insert(self.0, self.1);
}
}
fn horizontal_edges_contained(
poly: &Polygon,
a: Point2,
b: Point2,
cache: &mut HashMap<Point2, bool>,
edge_cache: &mut HashMap<(u64, (u64, u64)), bool>,
) -> bool {
assert_ne!(a.x, b.x);
let min_x = a.x.min(b.x);
@ -187,14 +213,30 @@ fn horizontal_edges_contained(poly: &Polygon, a: Point2, b: Point2) -> bool {
let y_values: &[u64] = if a.y == b.y { &[a.y] } else { &[a.y, b.y] };
let mut contains = |point: Point2| match cache.get(&point) {
Some(&v) => v,
None => {
let v = poly.contains(point);
cache.insert(point, v);
v
}
};
for &y in y_values {
let key = (y, (min_x, max_x));
if let Some(&v) = edge_cache.get(&key) {
return v;
}
let guard = InsertGuard(key, false, edge_cache);
// Left corner
if !poly.contains(Point2 { x: min_x, y }) {
if !contains(Point2 { x: min_x, y }) {
return false;
}
if !poly.contains(Point2 { x: min_x + 1, y }) {
if !contains(Point2 { x: min_x + 1, y }) {
return false;
}
@ -206,9 +248,9 @@ fn horizontal_edges_contained(poly: &Polygon, a: Point2, b: Point2) -> bool {
.skip_while(|&&x| x <= min_x)
.take_while(|&&x| x < max_x)
{
if !poly.contains(Point2 { x, y })
|| !poly.contains(Point2 { x: x - 1, y })
|| !poly.contains(Point2 { x: x + 1, y })
if !contains(Point2 { x, y })
|| !contains(Point2 { x: x - 1, y })
|| !contains(Point2 { x: x + 1, y })
{
return false;
}
@ -216,19 +258,28 @@ fn horizontal_edges_contained(poly: &Polygon, a: Point2, b: Point2) -> bool {
// Right corner
if !poly.contains(Point2 { x: max_x, y }) {
if !contains(Point2 { x: max_x, y }) {
return false;
}
if !poly.contains(Point2 { x: max_x - 1, y }) {
if !contains(Point2 { x: max_x - 1, y }) {
return false;
}
std::mem::forget(guard);
edge_cache.insert(key, true);
}
true
}
fn vertical_edges_contain(poly: &Polygon, a: Point2, b: Point2) -> bool {
fn vertical_edges_contain(
poly: &Polygon,
a: Point2,
b: Point2,
cache: &mut HashMap<Point2, bool>,
edge_cache: &mut HashMap<(u64, (u64, u64)), bool>,
) -> bool {
assert_ne!(a.y, b.y);
let min_y = a.y.min(b.y);
@ -236,14 +287,30 @@ fn vertical_edges_contain(poly: &Polygon, a: Point2, b: Point2) -> bool {
let x_values: &[u64] = if a.x == b.x { &[a.x] } else { &[a.x, b.x] };
let mut contains = |point: Point2| match cache.get(&point) {
Some(&v) => v,
None => {
let v = poly.contains(point);
cache.insert(point, v);
v
}
};
for &x in x_values {
let key = (x, (min_y, max_y));
if let Some(&v) = edge_cache.get(&key) {
return v;
}
let guard = InsertGuard(key, false, edge_cache);
// Bottom corner
if !poly.contains(Point2 { x, y: min_y }) {
if !contains(Point2 { x, y: min_y }) {
return false;
}
if !poly.contains(Point2 { x, y: min_y + 1 }) {
if !contains(Point2 { x, y: min_y + 1 }) {
return false;
}
@ -255,9 +322,9 @@ fn vertical_edges_contain(poly: &Polygon, a: Point2, b: Point2) -> bool {
.skip_while(|&&y| y <= min_y)
.take_while(|&&y| y < max_y)
{
if !poly.contains(Point2 { x, y })
|| !poly.contains(Point2 { x, y: y - 1 })
|| !poly.contains(Point2 { x, y: y + 1 })
if !contains(Point2 { x, y })
|| !contains(Point2 { x, y: y - 1 })
|| !contains(Point2 { x, y: y + 1 })
{
return false;
}
@ -265,13 +332,16 @@ fn vertical_edges_contain(poly: &Polygon, a: Point2, b: Point2) -> bool {
// Top corner
if !poly.contains(Point2 { x, y: max_y }) {
if !contains(Point2 { x, y: max_y }) {
return false;
}
if !poly.contains(Point2 { x, y: max_y - 1 }) {
if !contains(Point2 { x, y: max_y - 1 }) {
return false;
}
std::mem::forget(guard);
edge_cache.insert(key, true);
}
true
@ -283,6 +353,10 @@ pub fn part2(input: Parsed) {
let mut max_area = 0;
let mut contains_cache = HashMap::new();
let mut h_edge_cache = HashMap::new();
let mut v_edge_cache = HashMap::new();
for (ia, &a) in poly.points().iter().enumerate() {
for &b in poly.points().iter().skip(ia + 1) {
let area = rect_area(&a, &b);
@ -293,11 +367,15 @@ pub fn part2(input: Parsed) {
assert!(area > 1);
if a.x != b.x && !horizontal_edges_contained(&poly, a, b) {
if a.x != b.x
&& !horizontal_edges_contained(&poly, a, b, &mut contains_cache, &mut h_edge_cache)
{
continue;
}
if a.y != b.y && !vertical_edges_contain(&poly, a, b) {
if a.y != b.y
&& !vertical_edges_contain(&poly, a, b, &mut contains_cache, &mut v_edge_cache)
{
continue;
}