day9: still not working but corner-based algorithm

This commit is contained in:
2025-12-09 11:36:10 -08:00
parent 33275b3918
commit 72d7dc1cbb
3 changed files with 657 additions and 35 deletions

303
Cargo.lock generated
View File

@@ -66,9 +66,12 @@ dependencies = [
"aoc-runner-derive",
"cached",
"grid",
"indicatif",
"itertools",
"misc",
"rayon",
"regex",
"rstest",
]
[[package]]
@@ -92,7 +95,7 @@ dependencies = [
"ahash",
"cached_proc_macro",
"cached_proc_macro_types",
"hashbrown",
"hashbrown 0.15.5",
"once_cell",
"thiserror",
"web-time",
@@ -122,6 +125,44 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "console"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4"
dependencies = [
"encode_unicode",
"libc",
"once_cell",
"unicode-width",
"windows-sys",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "darling"
version = "0.20.11"
@@ -163,6 +204,12 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -181,6 +228,55 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "grid"
version = "0.1.0"
@@ -196,12 +292,42 @@ dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indexmap"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
]
[[package]]
name = "indicatif"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88"
dependencies = [
"console",
"portable-atomic",
"rayon",
"unicode-width",
"unit-prefix",
"web-time",
]
[[package]]
name = "intrusive-collections"
version = "0.9.7"
@@ -236,6 +362,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "memchr"
version = "2.7.6"
@@ -274,6 +406,33 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "proc-macro-crate"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.103"
@@ -292,6 +451,26 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "regex"
version = "1.12.2"
@@ -321,6 +500,50 @@ version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "relative-path"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "rstest"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49"
dependencies = [
"futures-timer",
"futures-util",
"rstest_macros",
]
[[package]]
name = "rstest_macros"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0"
dependencies = [
"cfg-if",
"glob",
"proc-macro-crate",
"proc-macro2",
"quote",
"regex",
"relative-path",
"rustc_version",
"syn 2.0.111",
"unicode-ident",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustversion"
version = "1.0.22"
@@ -333,6 +556,12 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde"
version = "1.0.228"
@@ -375,6 +604,12 @@ dependencies = [
"serde_core",
]
[[package]]
name = "slab"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "strsim"
version = "0.11.1"
@@ -423,12 +658,54 @@ dependencies = [
"syn 2.0.111",
]
[[package]]
name = "toml_datetime"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.23.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832"
dependencies = [
"indexmap",
"toml_datetime",
"toml_parser",
"winnow",
]
[[package]]
name = "toml_parser"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
dependencies = [
"winnow",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-width"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]]
name = "unit-prefix"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
[[package]]
name = "version_check"
version = "0.9.5"
@@ -490,6 +767,30 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "winnow"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
dependencies = [
"memchr",
]
[[package]]
name = "zerocopy"
version = "0.8.31"

View File

@@ -8,9 +8,12 @@ aoc-runner = "0.3.0"
aoc-runner-derive = "0.3.0"
cached = "0.56.0"
grid = {version = "0.1.0", path = "utils/grid"}
indicatif = { version = "0.18.3", features = ["rayon"] }
itertools = "0.14.0"
misc = {path = "utils/misc"}
rayon = "1.11.0"
regex = "1.11.1"
rstest = "0.26.1"
[profile.release]
lto = true

View File

@@ -1,11 +1,24 @@
use aoc_runner_derive::{aoc, aoc_generator};
use grid::{AsCoord2d, Coord2d, Grid};
use indicatif::{ParallelProgressIterator, ProgressIterator, ProgressStyle};
use itertools::Itertools;
use std::cmp::{max, min};
use rayon::prelude::*;
use std::collections::HashMap;
use std::{
cmp::{max, min},
fmt::Display,
};
#[aoc_generator(day9, part1)]
#[aoc_generator(day9)]
fn parse(input: &str) -> Vec<Coord2d> {
input.lines().map(|l| l.parse().unwrap()).collect()
input
.lines()
.map(|l| l.parse::<Coord2d>().unwrap())
.map(|c| Coord2d {
x: c.x + 1, // allow a buffer zone
y: c.y + 1,
})
.collect()
}
#[aoc(day9, part1)]
@@ -25,15 +38,13 @@ fn part1(input: &Vec<Coord2d>) -> u64 {
.unwrap()
}
#[aoc_generator(day9, part2)]
fn parse2(input: &str) -> (Vec<Coord2d>, Grid<u8>) {
let reds = parse(input);
let width = reds.iter().map(|c| c.x()).max().unwrap() + 1;
let height = reds.iter().map(|c| c.y()).max().unwrap() + 1;
fn build_grid(reds: &Vec<Coord2d>) -> Grid<u8> {
let width = reds.iter().map(|c| c.x()).max().unwrap() + 3;
let height = reds.iter().map(|c| c.y()).max().unwrap() + 3;
let mut grid = Grid::with_shape(width as usize, height as usize, b'.');
let mut prev = reds.last().unwrap();
for c in &reds {
for c in reds {
// mark c filled
grid.set(&c, b'#');
// build a line of green between it and the previous
@@ -52,33 +63,59 @@ fn parse2(input: &str) -> (Vec<Coord2d>, Grid<u8>) {
}
prev = c
}
println!("grid {}x{}", grid.width(), grid.height());
(reds, grid)
grid
}
fn build_hashgrid(reds: &Vec<Coord2d>) -> HashMap<Coord2d, u8> {
let mut grid = HashMap::new();
let mut prev = reds.last().unwrap();
for c in reds {
// mark c filled
grid.insert(*c, b'#');
// build a line of green between it and the previous
if c.x() == prev.x() {
// vertical
for y in (min(c.y(), prev.y()) + 1)..max(c.y(), prev.y()) {
grid.insert(Coord2d { x: c.x(), y }, b'X');
}
} else if c.y() == prev.y() {
// horiztonal
for x in (min(c.x(), prev.x()) + 1)..max(c.x(), prev.x()) {
grid.insert(Coord2d { x, y: c.y() }, b'X');
}
} else {
panic!()
}
prev = c
}
grid
}
// FIXME: TOTALLY BROKEN
fn flood_fill(grid: &mut Grid<u8>) {
#[derive(Debug, Eq, PartialEq)]
enum State {
OffLine(bool), // Off a line(true=inside)
OnLine(bool), // On a line(previous state)
}
for y in 1..grid.height() - 1 {
let mut state = match grid.get(&(0, y)) {
Some(b'.') => State::OffLine(false), //noop
Some(b'#') | Some(b'X') => State::OnLine(true),
s => panic!("Unexpected state: {s:?}"),
}; // if the row starts with a ., we start outside
for x in 1..grid.width() - 1 {
for y in 0..grid.height() {
let mut state = State::OffLine(false);
for x in 0..grid.width() {
match grid.get(&(x, y)) {
Some(b'.') => {
if state == State::OffLine(true) || state == State::OnLine(false) {
grid.set(&(x, y), b'X');
state = State::OffLine(true)
} else {
state = State::OffLine(false)
}
}
Some(b'#') | Some(b'X') => {
state = State::OnLine(match state {
State::OnLine(s) | State::OffLine(s) => s,
State::OnLine(s) => s,
State::OffLine(s) => s,
})
}
None => panic!("overran the grid"),
@@ -88,30 +125,219 @@ fn flood_fill(grid: &mut Grid<u8>) {
}
}
#[aoc(day9, part2)]
fn part2((reds, grid): &(Vec<Coord2d>, Grid<u8>)) -> u64 {
let mut grid = grid.clone();
// #[aoc(day9, part2, Brute)]
fn part2(reds: &Vec<Coord2d>) -> u64 {
let mut grid = build_grid(reds);
flood_fill(&mut grid);
println!("{grid}");
'outer: for (a, b) in reds
let pairs = reds
.iter()
.tuple_combinations()
.sorted_unstable_by_key(|(a, b)| (a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1))
.rev()
{
println!(
"{a} vs {b} = {}",
a.x().abs_diff(b.x()) * a.y().abs_diff(b.y())
);
.progress()
.collect_vec();
let (a, b) = pairs
.par_iter()
.progress_with_style(
ProgressStyle::with_template(
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
)
.unwrap(),
)
.filter(|(a, b)| (a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1) < 2972065369) // FIXME: PROGRESS CAPTURE
.map(|(a, b)| {
for y in (min(a.y(), b.y()))..=max(a.y(), b.y()) {
for x in (min(a.x(), b.x()))..=max(a.x(), b.y()) {
if *grid.get(&(x, y)).unwrap() == b'.' {
continue 'outer;
return (false, a, b);
}
}
}
return (a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1);
(true, a, b)
})
.find_map_first(|(good, a, b)| if good { Some((a, b)) } else { None })
.unwrap();
println!("win: {a} {b}");
(a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1)
}
fn line_square_intersection<LT: AsCoord2d, ST: AsCoord2d>(
&(l1, l2): &(&LT, &LT),
&(sq1, sq2): &(&ST, &ST),
) -> bool
where
LT: Clone + Display,
ST: Clone + Display,
{
let min_x = min(sq1.x(), sq2.x());
let max_x = max(sq1.x(), sq2.x());
let min_y = min(sq1.y(), sq2.y());
let max_y = max(sq1.y(), sq2.y());
// println!("({},{}) X [{},{}]", l1, l2, sq1, sq2);
// line is horizontal
if l1.y() == l2.y() {
// println!(" horizontal at y={}", l1.y());
// above, below, or touching square
if l1.y() <= min_y || l1.y() >= max_y {
// println!(" y out of range or touching");
false
// start inside
} else if min(l1.x(), l2.x()) >= min_x && min(l1.x(), l2.x()) <= max_x {
// println!(" start inside");
true
//end inside
} else if max(l1.x(), l2.x()) >= min_x && max(l1.x(), l2.x()) <= max_x {
// println!(" end inside");
true
} else {
// println!(" no overlap on x");
false
}
// line is vertical
} else {
// println!(" vertical at x={}", l1.x());
if l1.x() <= min_x || l1.x() >= max_x {
// println!(" x out of range or touching");
false
// start inside
} else if min(l1.y(), l2.y()) >= min_y && min(l1.y(), l2.y()) <= max_y {
// println!(" start inside");
true
//end inside
} else if max(l1.y(), l2.y()) >= min_y && max(l1.y(), l2.y()) <= max_y {
// println!(" end inside");
true
} else {
// println!(" no overlap on y");
false
}
}
}
trait Rectangle {
fn top_left(&self) -> Coord2d;
fn bottom_right(&self) -> Coord2d;
fn in_bounds<T: AsCoord2d>(&self, b: &T) -> bool {
b.x() >= self.top_left().x
&& b.x() <= self.bottom_right().x()
&& b.y() >= self.top_left().y()
&& b.y() <= self.bottom_right().y()
}
fn area(&self) -> u64 {
let a = self.top_left();
let b = self.bottom_right();
(a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1)
}
}
impl Rectangle for (&Coord2d, &Coord2d) {
fn top_left(&self) -> Coord2d {
Coord2d {
x: min(self.0.x, self.1.x),
y: min(self.0.y, self.1.y),
}
}
fn bottom_right(&self) -> Coord2d {
Coord2d {
x: max(self.0.x, self.1.x),
y: max(self.0.y, self.1.y),
}
}
}
// true = clockwise
fn outside_points(prev: &Coord2d, cur: &Coord2d, next: &Coord2d) -> Vec<Coord2d> {
let l1_diff = ((cur.x - prev.x).signum(), (cur.y - prev.y).signum());
let l2_diff = ((next.x - cur.x).signum(), (next.y - cur.y).signum());
match (l1_diff, l2_diff) {
// CW
((1, 0), (0, 1)) => vec![cur + (0, -1), cur + (1, -1), cur + (1, 0)], // x^ y^
((0, 1), (-1, 0)) => vec![cur + (1, 0), cur + (1, 1), cur + (0, -1)], // y^ xv
((-1, 0), (0, -1)) => vec![cur + (0, 1), cur + (-1, 1), cur + (-1, 0)], // xv yv
((0, -1), (1, 0)) => vec![cur + (-1, 0), cur + (-1, -1), cur + (0, -1)], // yv x^
// CCW
((0, 1), (1, 0)) => vec![cur + (1, -1)],
((1, 0), (0, -1)) => vec![cur + (-1, -1)],
((0, -1), (-1, 0)) => vec![cur + (-1, 1)],
((-1, 0), (0, 1)) => vec![cur + (1, 1)],
((0, _), (0, _)) | ((_, 0), (_, 0)) => vec![], // colinear
_ => panic!(
"unexpected line arrangement {:?} {:?} @ [({},{}), ({},{}), ({},{})",
l1_diff, l2_diff, prev.x, prev.y, cur.x, cur.y, next.x, next.y
),
}
}
#[aoc(day9, part2, Lines)]
fn part2_lines(reds: &Vec<Coord2d>) -> u64 {
// let mut grid = build_hashgrid(reds);
let mut grid = build_grid(reds);
println!("{grid}");
let outside_points: HashMap<&Coord2d, Vec<Coord2d>> = reds
.iter()
.circular_tuple_windows()
.map(|(a, b, c)| (b, outside_points(a, b, c)))
.collect();
// for corner in reds {
// for p in &outside_points[corner] {
// if *grid.get(p).unwrap() == b'.' {
// grid.set(p, b'O');
// }
// }
// }
// println!("{grid}");
// still iterate over the possible rectangles in size order
for rect in reds
.iter()
.tuple_combinations()
.sorted_unstable_by_key(|rect: &(&Coord2d, &Coord2d)| rect.area())
.rev()
.progress_with_style(
ProgressStyle::with_template(
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
)
.unwrap(),
)
{
println!(
"[{},{}] = {}",
rect.top_left(),
rect.bottom_right(),
rect.area()
);
// then for each corner, check if any of its outside edges are inside and not part of another line
if reds
.iter()
.filter(|corner| rect.in_bounds(corner))
.inspect(|c| println!(" corner: {c}"))
.any(|corner| {
outside_points[corner]
.iter()
.inspect(|p| println!(" {} = {}", p, *grid.get(p).unwrap() as char))
.any(|p| rect.in_bounds(p) && *grid.get(p).unwrap() == b'.')
})
{
continue;
}
println!(
"win! {},{} = {}",
rect.top_left(),
rect.bottom_right(),
rect.area()
);
grid.set(&rect.top_left(), b'*');
grid.set(&rect.bottom_right(), b'*');
println!("{grid}");
return rect.area();
}
panic!()
}
@@ -119,6 +345,7 @@ fn part2((reds, grid): &(Vec<Coord2d>, Grid<u8>)) -> u64 {
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
const EXAMPLE: &str = "7,1
11,1
@@ -136,6 +363,97 @@ mod tests {
#[test]
fn part2_example() {
assert_eq!(part2(&parse2(EXAMPLE)), 24);
assert_eq!(part2(&parse(EXAMPLE)), 24);
}
#[rstest]
#[case(
"7,1
11,1
11,7
9,7
9,5
2,5
2,3
7,3",
24
)]
#[case(
"4,2
13,2
13,4
8,4
8,6
11,6
11,10
4,10",
40
)]
#[case(
"3,2
17,2
17,13
13,13
13,11
15,11
15,8
11,8
11,15
18,15
18,17
4,17
4,12
6,12
6,5
3,5",
66
)]
#[case(
"2,2
8,2
8,6
5,6
5,4
2,4",
21
)]
#[case(
"3,1
12,1
12,4
9,4
9,8
3,8",
56
)]
#[case(
"7,1
11,1
11,3
13,3
13,5
11,5
11,7
9,7
9,5
2,5
2,3
7,3",
36
)]
#[case(
"1,2
8,2
8,4
6,4
6,5
9,5
9,8
1,8",
56
)]
fn part2_lines_example(#[case] input: &str, #[case] answer: u64) {
assert_eq!(part2_lines(&parse(input)), answer);
}
}