Day 9 part 2

This commit is contained in:
Quentin Boyer 2025-12-10 01:11:32 +01:00
parent cc2e040332
commit 3ba0a0b6c6

View file

@ -1,8 +1,9 @@
use std::time::Instant; use std::{collections::HashSet, ops::RangeInclusive, time::Instant};
use aoc_2025::{load, print_res}; use aoc_2025::{load, print_res};
use bstr::BString; use bstr::BString;
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
pub struct Point2 { pub struct Point2 {
x: u64, x: u64,
y: u64, y: u64,
@ -26,22 +27,285 @@ pub fn parsing(input: &BString) -> color_eyre::Result<Parsed> {
.collect() .collect()
} }
fn rect_area(a: &Point2, b: &Point2) -> u64 {
(a.x.abs_diff(b.x) + 1) * (a.y.abs_diff(b.y) + 1)
}
#[inline(never)] #[inline(never)]
pub fn part1(input: Parsed) { pub fn part1(input: Parsed) {
let max_area = input let max_area = input
.iter() .iter()
.enumerate() .enumerate()
.flat_map(|(ia, a)| input.iter().skip(ia + 1).map(move |b| (a, b))) .flat_map(|(ia, a)| input.iter().skip(ia + 1).map(move |b| (a, b)))
.map(|(a, b)| (a.x.abs_diff(b.x) + 1) * (a.y.abs_diff(b.y) + 1)) .map(|(a, b)| rect_area(a, b))
.max() .max()
.unwrap(); .unwrap();
print_res!("Max area: {max_area}"); print_res!("Max area: {max_area}");
} }
struct Polygon {
points: Vec<Point2>,
x_values: Vec<u64>,
y_values: Vec<u64>,
vertical_edges_by_x_slot: Vec<Vec<RangeInclusive<u64>>>,
horizontal_edges_by_y_slot: Vec<Vec<RangeInclusive<u64>>>,
}
impl Polygon {
pub fn new(points: Vec<Point2>) -> Self {
let x_values = {
let x: HashSet<_> = points.iter().map(|p| p.x).collect();
let mut x: Vec<_> = x.into_iter().collect();
x.sort_unstable();
x
};
let y_values = {
let y: HashSet<_> = points.iter().map(|p| p.y).collect();
let mut y: Vec<_> = y.into_iter().collect();
y.sort_unstable();
y
};
let mut this = Polygon {
points,
vertical_edges_by_x_slot: vec![Vec::new(); x_values.len()],
horizontal_edges_by_y_slot: vec![Vec::new(); y_values.len()],
x_values,
y_values,
};
for (i, p) in this.points.iter().enumerate() {
let prev = this.prev(i);
if prev.x == p.x {
let x_stop = this.x_values.iter().position(|&x| x == p.x).unwrap();
// Vertical edge
let min_y = p.y.min(prev.y);
let max_y = p.y.max(prev.y);
this.vertical_edges_by_x_slot[x_stop].push(min_y..=max_y);
} else {
let y_stop = this.y_values.iter().position(|&y| y == p.y).unwrap();
assert_eq!(prev.y, p.y);
let min_x = p.x.min(prev.x);
let max_x = p.x.max(prev.x);
this.horizontal_edges_by_y_slot[y_stop].push(min_x..=max_x);
}
}
this
}
pub fn contains(&self, point: Point2) -> bool {
// Is the point a corner?
if self.points.contains(&point) {
return true;
}
// On a horizontal edge
if let Some(y_stop) = self.y_values.iter().position(|&y| y == point.y) {
let edges = &self.horizontal_edges_by_y_slot[y_stop];
if edges.iter().any(|e| e.contains(&point.x)) {
return true;
}
}
// On a vertical edge
if let Some(x_stop) = self.x_values.iter().position(|&x| x == point.x) {
let edges = &self.vertical_edges_by_x_slot[x_stop];
if edges.iter().any(|e| e.contains(&point.y)) {
return true;
}
}
// Not on the boundary
let mut intersection_count = 0;
// Check all vertical edges starting from this point
for (slot, _x) in self
.x_values
.iter()
.enumerate()
.skip_while(|&(_, &x)| x < point.x)
.take_while(|&(_, &x)| point.x <= x)
{
let edges = &self.vertical_edges_by_x_slot[slot];
if let Some(edge) = edges.iter().find(|r| r.contains(&point.y)) {
let intersect = if point.y == *edge.start() {
*edge.end() > point.y
} else if point.y == *edge.end() {
*edge.start() > point.y
} else {
true
};
if intersect {
intersection_count += 1;
}
}
}
intersection_count % 2 == 1
}
pub fn points(&self) -> &[Point2] {
&self.points
}
pub fn x_values(&self) -> &[u64] {
&self.x_values
}
pub fn y_values(&self) -> &[u64] {
&self.y_values
}
pub fn prev(&self, index: usize) -> Point2 {
if index == 0 {
*self.points.last().unwrap()
} else {
self.points[index - 1]
}
}
}
fn horizontal_edges_contained(poly: &Polygon, a: Point2, b: Point2) -> bool {
assert_ne!(a.x, b.x);
let min_x = a.x.min(b.x);
let max_x = a.x.max(b.x);
let y_values: &[u64] = if a.y == b.y { &[a.y] } else { &[a.y, b.y] };
for &y in y_values {
// Left corner
if !poly.contains(Point2 { x: min_x, y }) {
return false;
}
if !poly.contains(Point2 { x: min_x + 1, y }) {
return false;
}
// Inside edges
for &x in poly
.x_values()
.iter()
.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 })
{
return false;
}
}
// Right corner
if !poly.contains(Point2 { x: max_x, y }) {
return false;
}
if !poly.contains(Point2 { x: max_x - 1, y }) {
return false;
}
}
true
}
fn vertical_edges_contain(poly: &Polygon, a: Point2, b: Point2) -> bool {
assert_ne!(a.y, b.y);
let min_y = a.y.min(b.y);
let max_y = a.y.max(b.y);
let x_values: &[u64] = if a.x == b.x { &[a.x] } else { &[a.x, b.x] };
for &x in x_values {
// Bottom corner
if !poly.contains(Point2 { x, y: min_y }) {
return false;
}
if !poly.contains(Point2 { x, y: min_y + 1 }) {
return false;
}
// Inside edges
for &y in poly
.y_values()
.iter()
.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 })
{
return false;
}
}
// Top corner
if !poly.contains(Point2 { x, y: max_y }) {
return false;
}
if !poly.contains(Point2 { x, y: max_y - 1 }) {
return false;
}
}
true
}
#[inline(never)] #[inline(never)]
pub fn part2(input: Parsed) { pub fn part2(input: Parsed) {
todo!("todo part2") let poly = Polygon::new(input);
let mut max_area = 0;
for (ia, &a) in poly.points().iter().enumerate() {
for &b in poly.points().iter().skip(ia + 1) {
let area = rect_area(&a, &b);
// Area would not even be bigger, dont attempt to validate it
if area < max_area {
continue;
}
assert!(area > 1);
if a.x != b.x && !horizontal_edges_contained(&poly, a, b) {
continue;
}
if a.y != b.y && !vertical_edges_contain(&poly, a, b) {
continue;
}
max_area = area;
}
}
print_res!("Max inside area: {max_area}");
} }
pub fn main() -> color_eyre::Result<()> { pub fn main() -> color_eyre::Result<()> {