Cache the polygon containement

This commit is contained in:
Quentin Boyer 2025-12-10 01:20:35 +01:00
parent 3ba0a0b6c6
commit 8662ab104f

View file

@ -1,4 +1,8 @@
use std::{collections::HashSet, ops::RangeInclusive, time::Instant}; use std::{
collections::{HashMap, HashSet},
ops::RangeInclusive,
time::Instant,
};
use aoc_2025::{load, print_res}; use aoc_2025::{load, print_res};
use bstr::BString; use bstr::BString;
@ -179,7 +183,12 @@ impl Polygon {
} }
} }
fn horizontal_edges_contained(poly: &Polygon, a: Point2, b: Point2) -> bool { fn horizontal_edges_contained(
poly: &Polygon,
a: Point2,
b: Point2,
cache: &mut HashMap<Point2, bool>,
) -> bool {
assert_ne!(a.x, b.x); assert_ne!(a.x, b.x);
let min_x = a.x.min(b.x); let min_x = a.x.min(b.x);
@ -187,14 +196,23 @@ 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 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 { for &y in y_values {
// Left corner // Left corner
if !poly.contains(Point2 { x: min_x, y }) { if !contains(Point2 { x: min_x, y }) {
return false; return false;
} }
if !poly.contains(Point2 { x: min_x + 1, y }) { if !contains(Point2 { x: min_x + 1, y }) {
return false; return false;
} }
@ -206,9 +224,9 @@ fn horizontal_edges_contained(poly: &Polygon, a: Point2, b: Point2) -> bool {
.skip_while(|&&x| x <= min_x) .skip_while(|&&x| x <= min_x)
.take_while(|&&x| x < max_x) .take_while(|&&x| x < max_x)
{ {
if !poly.contains(Point2 { x, y }) if !contains(Point2 { x, y })
|| !poly.contains(Point2 { x: x - 1, y }) || !contains(Point2 { x: x - 1, y })
|| !poly.contains(Point2 { x: x + 1, y }) || !contains(Point2 { x: x + 1, y })
{ {
return false; return false;
} }
@ -216,11 +234,11 @@ fn horizontal_edges_contained(poly: &Polygon, a: Point2, b: Point2) -> bool {
// Right corner // Right corner
if !poly.contains(Point2 { x: max_x, y }) { if !contains(Point2 { x: max_x, y }) {
return false; return false;
} }
if !poly.contains(Point2 { x: max_x - 1, y }) { if !contains(Point2 { x: max_x - 1, y }) {
return false; return false;
} }
} }
@ -228,7 +246,12 @@ fn horizontal_edges_contained(poly: &Polygon, a: Point2, b: Point2) -> bool {
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>,
) -> bool {
assert_ne!(a.y, b.y); assert_ne!(a.y, b.y);
let min_y = a.y.min(b.y); let min_y = a.y.min(b.y);
@ -236,14 +259,23 @@ 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 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 { for &x in x_values {
// Bottom corner // Bottom corner
if !poly.contains(Point2 { x, y: min_y }) { if !contains(Point2 { x, y: min_y }) {
return false; return false;
} }
if !poly.contains(Point2 { x, y: min_y + 1 }) { if !contains(Point2 { x, y: min_y + 1 }) {
return false; return false;
} }
@ -255,9 +287,9 @@ fn vertical_edges_contain(poly: &Polygon, a: Point2, b: Point2) -> bool {
.skip_while(|&&y| y <= min_y) .skip_while(|&&y| y <= min_y)
.take_while(|&&y| y < max_y) .take_while(|&&y| y < max_y)
{ {
if !poly.contains(Point2 { x, y }) if !contains(Point2 { x, y })
|| !poly.contains(Point2 { x, y: y - 1 }) || !contains(Point2 { x, y: y - 1 })
|| !poly.contains(Point2 { x, y: y + 1 }) || !contains(Point2 { x, y: y + 1 })
{ {
return false; return false;
} }
@ -265,11 +297,11 @@ fn vertical_edges_contain(poly: &Polygon, a: Point2, b: Point2) -> bool {
// Top corner // Top corner
if !poly.contains(Point2 { x, y: max_y }) { if !contains(Point2 { x, y: max_y }) {
return false; return false;
} }
if !poly.contains(Point2 { x, y: max_y - 1 }) { if !contains(Point2 { x, y: max_y - 1 }) {
return false; return false;
} }
} }
@ -283,6 +315,8 @@ pub fn part2(input: Parsed) {
let mut max_area = 0; let mut max_area = 0;
let mut contains_cache = HashMap::new();
for (ia, &a) in poly.points().iter().enumerate() { for (ia, &a) in poly.points().iter().enumerate() {
for &b in poly.points().iter().skip(ia + 1) { for &b in poly.points().iter().skip(ia + 1) {
let area = rect_area(&a, &b); let area = rect_area(&a, &b);
@ -293,11 +327,11 @@ pub fn part2(input: Parsed) {
assert!(area > 1); 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) {
continue; 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) {
continue; continue;
} }