Solve 09
This commit is contained in:
parent
1ce82f6ac4
commit
c6d69e2fc2
2 changed files with 48 additions and 105 deletions
|
|
@ -9,3 +9,6 @@ cached = "0.56.0"
|
||||||
derive_more = { version = "2.1.0", features = ["deref"] }
|
derive_more = { version = "2.1.0", features = ["deref"] }
|
||||||
glam = "0.30.9"
|
glam = "0.30.9"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = true
|
||||||
|
|
|
||||||
150
src/bin/09.rs
150
src/bin/09.rs
|
|
@ -1,56 +1,15 @@
|
||||||
use aoc::*;
|
use aoc::*;
|
||||||
use derive_more::Deref;
|
use derive_more::Deref;
|
||||||
use glam::{I64Vec2, IVec2, USizeVec2};
|
use glam::I64Vec2;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::HashMap;
|
||||||
use std::ops::Sub;
|
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../../input/09");
|
const INPUT: &str = include_str!("../../input/09");
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let polygon = Polygon::parse(include_str!("../../input/09-test"));
|
println!("Part 1: {}", part1(INPUT));
|
||||||
// let polygon = Polygon::parse("2,2\n2,5\n5,5\n5,2\n");
|
println!("Part 2: {}", part2(INPUT));
|
||||||
// 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]
|
#[test]
|
||||||
|
|
@ -69,7 +28,7 @@ fn example() {
|
||||||
|
|
||||||
fn part1(input: &str) -> u64 {
|
fn part1(input: &str) -> u64 {
|
||||||
Polygon::parse(input)
|
Polygon::parse(input)
|
||||||
.0
|
.points
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.tuple_combinations()
|
.tuple_combinations()
|
||||||
.map(Rect::from)
|
.map(Rect::from)
|
||||||
|
|
@ -82,6 +41,7 @@ fn part2(input: &str) -> u64 {
|
||||||
let polygon = Polygon::parse(input);
|
let polygon = Polygon::parse(input);
|
||||||
let (polygon, uncompress) = polygon.compress();
|
let (polygon, uncompress) = polygon.compress();
|
||||||
polygon
|
polygon
|
||||||
|
.points
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.tuple_combinations()
|
.tuple_combinations()
|
||||||
|
|
@ -92,11 +52,14 @@ fn part2(input: &str) -> u64 {
|
||||||
})
|
})
|
||||||
.find(|&r| polygon.contains_rect(r))
|
.find(|&r| polygon.contains_rect(r))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.uncompress(&uncompress)
|
||||||
.area()
|
.area()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deref)]
|
struct Polygon {
|
||||||
struct Polygon(Vec<I64Vec2>);
|
points: Vec<I64Vec2>,
|
||||||
|
cached_vertical_lines: Vec<Line>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Uncompress a polygon with this.
|
/// Uncompress a polygon with this.
|
||||||
/// Maps from compressed coords to actual coords.
|
/// Maps from compressed coords to actual coords.
|
||||||
|
|
@ -107,12 +70,17 @@ struct Uncompress {
|
||||||
|
|
||||||
impl Polygon {
|
impl Polygon {
|
||||||
fn parse(input: &str) -> Self {
|
fn parse(input: &str) -> Self {
|
||||||
Self(input.lines().map(parse_line).collect())
|
let mut polygon = Self {
|
||||||
|
points: input.lines().map(parse_line).collect(),
|
||||||
|
cached_vertical_lines: vec![],
|
||||||
|
};
|
||||||
|
polygon.cache_vertical_lines();
|
||||||
|
polygon
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compress(mut self) -> (Self, Uncompress) {
|
fn compress(mut self) -> (Self, Uncompress) {
|
||||||
let compress_x: HashMap<i64, i64> = self
|
let compress_x: HashMap<i64, i64> = self
|
||||||
.0
|
.points
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| p.x)
|
.map(|p| p.x)
|
||||||
.unique()
|
.unique()
|
||||||
|
|
@ -121,7 +89,7 @@ impl Polygon {
|
||||||
.map(|(i, x)| (x, i as i64))
|
.map(|(i, x)| (x, i as i64))
|
||||||
.collect();
|
.collect();
|
||||||
let compress_y: HashMap<i64, i64> = self
|
let compress_y: HashMap<i64, i64> = self
|
||||||
.0
|
.points
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| p.y)
|
.map(|p| p.y)
|
||||||
.unique()
|
.unique()
|
||||||
|
|
@ -129,11 +97,14 @@ impl Polygon {
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, y)| (y, i as i64))
|
.map(|(i, y)| (y, i as i64))
|
||||||
.collect();
|
.collect();
|
||||||
self.0 = self
|
|
||||||
.0
|
self.points = self
|
||||||
|
.points
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| I64Vec2::new(compress_x[&p.x], compress_y[&p.y]))
|
.map(|p| I64Vec2::new(compress_x[&p.x], compress_y[&p.y]))
|
||||||
.collect();
|
.collect();
|
||||||
|
self.cache_vertical_lines();
|
||||||
|
|
||||||
let uncompress_x = compress_x.into_iter().map(|(x, i)| (i, x)).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();
|
let uncompress_y = compress_y.into_iter().map(|(y, i)| (i, y)).collect();
|
||||||
|
|
||||||
|
|
@ -154,20 +125,24 @@ impl Polygon {
|
||||||
fn contains_point(&self, p: I64Vec2) -> bool {
|
fn contains_point(&self, p: I64Vec2) -> bool {
|
||||||
// Interesting values for x
|
// Interesting values for x
|
||||||
let xs = self
|
let xs = self
|
||||||
.vertical_lines()
|
.cached_vertical_lines
|
||||||
.into_iter()
|
.iter()
|
||||||
.filter(|l| {
|
.filter(|l| {
|
||||||
let ymin = l.0.y.min(l.1.y);
|
let ymin = l.0.y.min(l.1.y);
|
||||||
let ymax = l.0.y.max(l.1.y);
|
let ymax = l.0.y.max(l.1.y);
|
||||||
ymin <= p.y && p.y <= ymax
|
ymin <= p.y && p.y <= ymax
|
||||||
})
|
})
|
||||||
.flat_map(|l| [l.0.x - 1, l.0.x])
|
.flat_map(|l| [l.0.x])
|
||||||
.filter(|&x| x < p.x)
|
.filter(|&x| x < p.x)
|
||||||
.chain([p.x])
|
.chain([p.x])
|
||||||
.sorted();
|
.sorted();
|
||||||
|
|
||||||
let lines = self.vertical_lines();
|
let line = |p| {
|
||||||
let line = |p| lines.iter().find(|line| line.contains(p)).copied();
|
self.cached_vertical_lines
|
||||||
|
.iter()
|
||||||
|
.find(|line| line.contains(p))
|
||||||
|
.copied()
|
||||||
|
};
|
||||||
|
|
||||||
let mut winding = 0;
|
let mut winding = 0;
|
||||||
let mut on_line = false;
|
let mut on_line = false;
|
||||||
|
|
@ -186,8 +161,9 @@ impl Polygon {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vertical_lines(&self) -> Vec<Line> {
|
fn vertical_lines(&self) -> Vec<Line> {
|
||||||
self.iter()
|
self.points
|
||||||
.chain(self.first()) // close the loop
|
.iter()
|
||||||
|
.chain(self.points.first()) // close the loop
|
||||||
.copied()
|
.copied()
|
||||||
.tuple_windows()
|
.tuple_windows()
|
||||||
.map(|(a, b)| Line(a, b))
|
.map(|(a, b)| Line(a, b))
|
||||||
|
|
@ -195,28 +171,16 @@ impl Polygon {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn area(&self) -> HashSet<I64Vec2> {
|
fn cache_vertical_lines(&mut self) {
|
||||||
// let (xmin, xmax) = self.0.iter().map(|p| p.x).minmax().into_option().unwrap();
|
self.cached_vertical_lines = self.vertical_lines();
|
||||||
// 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, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct Line(I64Vec2, I64Vec2);
|
struct Line(I64Vec2, I64Vec2);
|
||||||
|
|
||||||
impl Line {
|
impl Line {
|
||||||
/// true if p on Line(a, b)
|
/// true if p on line
|
||||||
fn contains(self, p: I64Vec2) -> bool {
|
fn contains(self, p: I64Vec2) -> bool {
|
||||||
if self.is_vertical() {
|
if self.is_vertical() {
|
||||||
let in_plane = p.x == self.0.x;
|
let in_plane = p.x == self.0.x;
|
||||||
|
|
@ -231,10 +195,6 @@ impl Line {
|
||||||
let in_bounds = xmin <= p.x && p.x <= xmax;
|
let in_bounds = xmin <= p.x && p.x <= xmax;
|
||||||
in_bounds && in_plane
|
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 {
|
fn is_vertical(self) -> bool {
|
||||||
|
|
@ -260,12 +220,9 @@ impl Line {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uncompress_i64vec2(v: I64Vec2, uncompress: &Uncompress) -> I64Vec2 {
|
|
||||||
I64Vec2::new(uncompress.x[&v.x], uncompress.y[&v.y])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct Rect(I64Vec2, I64Vec2);
|
struct Rect(I64Vec2, I64Vec2);
|
||||||
|
|
||||||
impl Rect {
|
impl Rect {
|
||||||
fn area(self) -> u64 {
|
fn area(self) -> u64 {
|
||||||
(self.0.x.abs_diff(self.1.x) + 1) * (self.0.y.abs_diff(self.1.y) + 1)
|
(self.0.x.abs_diff(self.1.x) + 1) * (self.0.y.abs_diff(self.1.y) + 1)
|
||||||
|
|
@ -282,27 +239,6 @@ impl Rect {
|
||||||
Self(a, b)
|
Self(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
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(I64Vec2::new(x, y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lines(self) -> [Line; 4] {
|
fn lines(self) -> [Line; 4] {
|
||||||
let x1 = self.0.x.min(self.1.x);
|
let x1 = self.0.x.min(self.1.x);
|
||||||
let x2 = self.0.x.max(self.1.x);
|
let x2 = self.0.x.max(self.1.x);
|
||||||
|
|
@ -324,6 +260,10 @@ impl Rect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn uncompress_i64vec2(v: I64Vec2, uncompress: &Uncompress) -> I64Vec2 {
|
||||||
|
I64Vec2::new(uncompress.x[&v.x], uncompress.y[&v.y])
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_line(line: &str) -> I64Vec2 {
|
fn parse_line(line: &str) -> I64Vec2 {
|
||||||
let (x, y) = line.split_once(',').unwrap();
|
let (x, y) = line.split_once(',').unwrap();
|
||||||
I64Vec2::new(x.parse().unwrap(), y.parse().unwrap())
|
I64Vec2::new(x.parse().unwrap(), y.parse().unwrap())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue