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 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, 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<()> {
|
pub fn main() -> color_eyre::Result<()> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue