Grrr
This commit is contained in:
parent
f523a094c8
commit
1ce82f6ac4
1 changed files with 239 additions and 41 deletions
282
src/bin/09.rs
282
src/bin/09.rs
|
|
@ -1,23 +1,69 @@
|
|||
use aoc::*;
|
||||
use derive_more::Deref;
|
||||
use glam::U64Vec2;
|
||||
use glam::{I64Vec2, IVec2, USizeVec2};
|
||||
use itertools::Itertools;
|
||||
use std::cmp::Reverse;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ops::Sub;
|
||||
|
||||
const INPUT: &str = include_str!("../../input/09");
|
||||
|
||||
fn main() {
|
||||
println!("Part 1: {}", part1(INPUT));
|
||||
println!("Part 2: {}", part2(INPUT));
|
||||
let polygon = Polygon::parse(include_str!("../../input/09-test"));
|
||||
// let polygon = Polygon::parse("2,2\n2,5\n5,5\n5,2\n");
|
||||
// let polygon = Polygon::parse("0,0\n2,0\n2,2\n0,2\n");
|
||||
// for line in polygon.vertical_lines() {
|
||||
// println!("{line:?} {}", line.winding());
|
||||
// }
|
||||
let (polygon, uncompress) = polygon.compress();
|
||||
print_corners(&polygon);
|
||||
println!("===");
|
||||
print_area(&polygon);
|
||||
// println!("Part 1: {}", part1(INPUT));
|
||||
// let p2 = part2(INPUT);
|
||||
// println!("Part 2: {}", p2);
|
||||
// assert!(p2 < 4583207265);
|
||||
}
|
||||
|
||||
fn print_corners(p: &Polygon) {
|
||||
for y in 0..=10 {
|
||||
for x in 0..=20 {
|
||||
let draw = p.contains(&I64Vec2::new(x, y));
|
||||
if draw {
|
||||
print!("#");
|
||||
} else {
|
||||
print!(".");
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
fn print_area(p: &Polygon) {
|
||||
for y in 0..=10 {
|
||||
for x in 0..=20 {
|
||||
let draw = p.contains_point(I64Vec2::new(x, y));
|
||||
if draw {
|
||||
print!("#");
|
||||
} else {
|
||||
print!(".");
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example() {
|
||||
assert_example!(part1, "09-test", 50);
|
||||
assert!(Line(U64Vec2::new(0, 10), U64Vec2::ZERO).contains(U64Vec2::new(0, 0)));
|
||||
assert!(Line(U64Vec2::new(0, 10), U64Vec2::ZERO).contains(U64Vec2::new(0, 10)));
|
||||
assert!(!Line(U64Vec2::new(0, 10), U64Vec2::ZERO).contains(U64Vec2::new(0, 11)));
|
||||
assert!(!Line(U64Vec2::new(0, 10), U64Vec2::ZERO).contains(U64Vec2::new(1, 10)));
|
||||
assert!(Line(I64Vec2::new(0, 10), I64Vec2::ZERO).contains(I64Vec2::new(0, 0)));
|
||||
assert!(Line(I64Vec2::new(0, 10), I64Vec2::ZERO).contains(I64Vec2::new(0, 10)));
|
||||
assert!(Line(I64Vec2::new(0, 10), I64Vec2::ZERO).contains(I64Vec2::new(0, 5)));
|
||||
assert!(!Line(I64Vec2::new(0, 10), I64Vec2::ZERO).contains(I64Vec2::new(0, 11)));
|
||||
assert!(!Line(I64Vec2::new(0, 10), I64Vec2::ZERO).contains(I64Vec2::new(1, 10)));
|
||||
assert!(!Line(I64Vec2::ONE, I64Vec2::ONE).contains(I64Vec2::new(1, 10)));
|
||||
assert!(Line(I64Vec2::ONE, I64Vec2::ONE).contains(I64Vec2::new(1, 1)));
|
||||
assert!(!Line(I64Vec2::ONE, I64Vec2::ONE).contains(I64Vec2::ZERO));
|
||||
assert_example!(part2, "09-test", 24);
|
||||
}
|
||||
|
||||
|
|
@ -34,99 +80,251 @@ fn part1(input: &str) -> u64 {
|
|||
|
||||
fn part2(input: &str) -> u64 {
|
||||
let polygon = Polygon::parse(input);
|
||||
let (polygon, uncompress) = polygon.compress();
|
||||
polygon
|
||||
.iter()
|
||||
.copied()
|
||||
.tuple_combinations()
|
||||
.map(Rect::from)
|
||||
.filter(|&r| polygon.contains_rect(r))
|
||||
.map(Rect::area)
|
||||
.max()
|
||||
.sorted_by_key(|r| {
|
||||
let area = r.uncompress(&uncompress).area();
|
||||
Reverse(area)
|
||||
})
|
||||
.find(|&r| polygon.contains_rect(r))
|
||||
.unwrap()
|
||||
.area()
|
||||
}
|
||||
|
||||
#[derive(Deref)]
|
||||
struct Polygon(Vec<U64Vec2>);
|
||||
struct Polygon(Vec<I64Vec2>);
|
||||
|
||||
/// Uncompress a polygon with this.
|
||||
/// Maps from compressed coords to actual coords.
|
||||
struct Uncompress {
|
||||
x: HashMap<i64, i64>,
|
||||
y: HashMap<i64, i64>,
|
||||
}
|
||||
|
||||
impl Polygon {
|
||||
fn parse(input: &str) -> Self {
|
||||
Self(input.lines().map(parse_line).collect())
|
||||
}
|
||||
|
||||
fn compress(mut self) -> (Self, Uncompress) {
|
||||
let compress_x: HashMap<i64, i64> = self
|
||||
.0
|
||||
.iter()
|
||||
.map(|p| p.x)
|
||||
.unique()
|
||||
.sorted()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (x, i as i64))
|
||||
.collect();
|
||||
let compress_y: HashMap<i64, i64> = self
|
||||
.0
|
||||
.iter()
|
||||
.map(|p| p.y)
|
||||
.unique()
|
||||
.sorted()
|
||||
.enumerate()
|
||||
.map(|(i, y)| (y, i as i64))
|
||||
.collect();
|
||||
self.0 = self
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|p| I64Vec2::new(compress_x[&p.x], compress_y[&p.y]))
|
||||
.collect();
|
||||
let uncompress_x = compress_x.into_iter().map(|(x, i)| (i, x)).collect();
|
||||
let uncompress_y = compress_y.into_iter().map(|(y, i)| (i, y)).collect();
|
||||
|
||||
(
|
||||
self,
|
||||
Uncompress {
|
||||
x: uncompress_x,
|
||||
y: uncompress_y,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn contains_rect(&self, r: Rect) -> bool {
|
||||
r.all_points().into_iter().all(|p| self.contains_point(p))
|
||||
r.outline().into_iter().all(|p| self.contains_point(p))
|
||||
}
|
||||
|
||||
/// Raycast from left to right and check intersections with lines.
|
||||
fn contains_point(&self, p: U64Vec2) -> bool {
|
||||
let lines = self.lines();
|
||||
let is_on_line = |p| lines.iter().any(|line| line.contains(p));
|
||||
fn contains_point(&self, p: I64Vec2) -> bool {
|
||||
// Interesting values for x
|
||||
let xs = self
|
||||
.vertical_lines()
|
||||
.into_iter()
|
||||
.filter(|l| {
|
||||
let ymin = l.0.y.min(l.1.y);
|
||||
let ymax = l.0.y.max(l.1.y);
|
||||
ymin <= p.y && p.y <= ymax
|
||||
})
|
||||
.flat_map(|l| [l.0.x - 1, l.0.x])
|
||||
.filter(|&x| x < p.x)
|
||||
.chain([p.x])
|
||||
.sorted();
|
||||
|
||||
let mut inside = false;
|
||||
let mut was_on_line = false;
|
||||
let lines = self.vertical_lines();
|
||||
let line = |p| lines.iter().find(|line| line.contains(p)).copied();
|
||||
|
||||
for x in 0..=p.x {
|
||||
let check = U64Vec2::new(x, p.y);
|
||||
let is_on_line = is_on_line(check);
|
||||
let crossed = !is_on_line && was_on_line;
|
||||
if crossed {
|
||||
inside ^= true;
|
||||
let mut winding = 0;
|
||||
let mut on_line = false;
|
||||
|
||||
for x in xs {
|
||||
let check = I64Vec2::new(x, p.y);
|
||||
if let Some(line) = line(check) {
|
||||
winding = line.winding();
|
||||
on_line = true;
|
||||
} else {
|
||||
on_line = false;
|
||||
}
|
||||
was_on_line = is_on_line;
|
||||
}
|
||||
|
||||
inside || was_on_line
|
||||
winding < 0 || on_line
|
||||
}
|
||||
|
||||
fn lines(&self) -> Vec<Line> {
|
||||
fn vertical_lines(&self) -> Vec<Line> {
|
||||
self.iter()
|
||||
.chain(self.first()) // close the loop
|
||||
.copied()
|
||||
.tuple_combinations()
|
||||
.tuple_windows()
|
||||
.map(|(a, b)| Line(a, b))
|
||||
.filter(|l| l.is_vertical())
|
||||
.collect()
|
||||
}
|
||||
|
||||
// fn area(&self) -> HashSet<I64Vec2> {
|
||||
// let (xmin, xmax) = self.0.iter().map(|p| p.x).minmax().into_option().unwrap();
|
||||
// let (ymin, ymax) = self.0.iter().map(|p| p.y).minmax().into_option().unwrap();
|
||||
//
|
||||
// let mut result = HashSet::new();
|
||||
// for x in xmin..=xmax {
|
||||
// for y in ymin..=ymax {
|
||||
// let p = I64Vec2::new(x, y);
|
||||
// if self.contains_point(p) {
|
||||
// result.insert(p);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// result
|
||||
// }
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Line(U64Vec2, U64Vec2);
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Line(I64Vec2, I64Vec2);
|
||||
|
||||
impl Line {
|
||||
/// true if p on Line(a, b)
|
||||
fn contains(&self, p: U64Vec2) -> bool {
|
||||
let a = self.0.sub(p).length_squared();
|
||||
let b = self.1.sub(p).length_squared();
|
||||
let c = self.0.sub(self.1).length_squared();
|
||||
a + b == c
|
||||
fn contains(self, p: I64Vec2) -> bool {
|
||||
if self.is_vertical() {
|
||||
let in_plane = p.x == self.0.x;
|
||||
let ymin = self.0.y.min(self.1.y);
|
||||
let ymax = self.0.y.max(self.1.y);
|
||||
let in_bounds = ymin <= p.y && p.y <= ymax;
|
||||
in_bounds && in_plane
|
||||
} else {
|
||||
let in_plane = p.y == self.0.y;
|
||||
let xmin = self.0.x.min(self.1.x);
|
||||
let xmax = self.0.x.max(self.1.x);
|
||||
let in_bounds = xmin <= p.x && p.x <= xmax;
|
||||
in_bounds && in_plane
|
||||
}
|
||||
// let a = self.0.sub(p).length_squared();
|
||||
// let b = self.1.sub(p).length_squared();
|
||||
// let c = self.0.sub(self.1).length_squared();
|
||||
// a + b == c
|
||||
}
|
||||
|
||||
fn is_vertical(self) -> bool {
|
||||
self.0.x == self.1.x
|
||||
}
|
||||
|
||||
fn winding(self) -> i64 {
|
||||
(self.1.y - self.0.y).signum()
|
||||
}
|
||||
|
||||
fn all_points(self) -> Vec<I64Vec2> {
|
||||
if self.is_vertical() {
|
||||
let ymin = self.0.y.min(self.1.y);
|
||||
let ymax = self.0.y.max(self.1.y);
|
||||
let x = self.0.x;
|
||||
(ymin..=ymax).map(|y| I64Vec2::new(x, y)).collect()
|
||||
} else {
|
||||
let xmin = self.0.x.min(self.1.x);
|
||||
let xmax = self.0.x.max(self.1.x);
|
||||
let y = self.0.y;
|
||||
(xmin..=xmax).map(|x| I64Vec2::new(x, y)).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn uncompress_i64vec2(v: I64Vec2, uncompress: &Uncompress) -> I64Vec2 {
|
||||
I64Vec2::new(uncompress.x[&v.x], uncompress.y[&v.y])
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Rect(U64Vec2, U64Vec2);
|
||||
|
||||
struct Rect(I64Vec2, I64Vec2);
|
||||
impl Rect {
|
||||
fn area(self) -> u64 {
|
||||
(self.0.x.abs_diff(self.1.x) + 1) * (self.0.y.abs_diff(self.1.y) + 1)
|
||||
}
|
||||
|
||||
fn from((a, b): (U64Vec2, U64Vec2)) -> Self {
|
||||
fn uncompress(self, uncompress: &Uncompress) -> Self {
|
||||
Self(
|
||||
uncompress_i64vec2(self.0, uncompress),
|
||||
uncompress_i64vec2(self.1, uncompress),
|
||||
)
|
||||
}
|
||||
|
||||
fn from((a, b): (I64Vec2, I64Vec2)) -> Self {
|
||||
Self(a, b)
|
||||
}
|
||||
|
||||
fn all_points(self) -> Vec<U64Vec2> {
|
||||
fn corners(self) -> [I64Vec2; 4] {
|
||||
[
|
||||
I64Vec2::new(self.0.x, self.0.y),
|
||||
I64Vec2::new(self.1.x, self.0.y),
|
||||
I64Vec2::new(self.0.x, self.1.y),
|
||||
I64Vec2::new(self.1.x, self.1.y),
|
||||
]
|
||||
}
|
||||
|
||||
fn all_points(self) -> Vec<I64Vec2> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
for x in self.0.x.min(self.1.x)..=self.0.x.max(self.1.x) {
|
||||
for y in self.0.y.min(self.1.y)..=self.0.y.max(self.1.y) {
|
||||
result.push(U64Vec2::new(x, y));
|
||||
result.push(I64Vec2::new(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn lines(self) -> [Line; 4] {
|
||||
let x1 = self.0.x.min(self.1.x);
|
||||
let x2 = self.0.x.max(self.1.x);
|
||||
let y1 = self.0.y.min(self.1.y);
|
||||
let y2 = self.0.y.max(self.1.y);
|
||||
[
|
||||
Line(I64Vec2::new(x1, y1), I64Vec2::new(x2, y1)),
|
||||
Line(I64Vec2::new(x2, y1), I64Vec2::new(x2, y2)),
|
||||
Line(I64Vec2::new(x2, y2), I64Vec2::new(x1, y2)),
|
||||
Line(I64Vec2::new(x1, y2), I64Vec2::new(x1, y1)),
|
||||
]
|
||||
}
|
||||
|
||||
fn parse_line(line: &str) -> U64Vec2 {
|
||||
let (x, y) = line.split_once(',').unwrap();
|
||||
U64Vec2::new(x.parse().unwrap(), y.parse().unwrap())
|
||||
fn outline(self) -> Vec<I64Vec2> {
|
||||
self.lines()
|
||||
.into_iter()
|
||||
.flat_map(|l| l.all_points())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_line(line: &str) -> I64Vec2 {
|
||||
let (x, y) = line.split_once(',').unwrap();
|
||||
I64Vec2::new(x.parse().unwrap(), y.parse().unwrap())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue