Day 9 part 2
This commit is contained in:
parent
cc2e040332
commit
3ba0a0b6c6
1 changed files with 267 additions and 3 deletions
270
src/bin/day9.rs
270
src/bin/day9.rs
|
|
@ -1,8 +1,9 @@
|
|||
use std::time::Instant;
|
||||
use std::{collections::HashSet, ops::RangeInclusive, time::Instant};
|
||||
|
||||
use aoc_2025::{load, print_res};
|
||||
use bstr::BString;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
|
||||
pub struct Point2 {
|
||||
x: u64,
|
||||
y: u64,
|
||||
|
|
@ -26,22 +27,285 @@ pub fn parsing(input: &BString) -> color_eyre::Result<Parsed> {
|
|||
.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)]
|
||||
pub fn part1(input: Parsed) {
|
||||
let max_area = input
|
||||
.iter()
|
||||
.enumerate()
|
||||
.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()
|
||||
.unwrap();
|
||||
|
||||
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)]
|
||||
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, don’t 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<()> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue