day9: part1 and bruteforce part 2 (some cleanups and clippies)

This commit is contained in:
2025-12-08 22:17:10 -08:00
parent 3f9f7afd27
commit 33275b3918
6 changed files with 171 additions and 10 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -1,6 +1,6 @@
<!-- AOC TILES BEGIN -->
<h1 align="center">
2025 - 16 ⭐ - Rust
2025 - 17 ⭐ - Rust
</h1>
<a href="src/day1.rs">
<img src=".aoc_tiles/tiles/2025/01.png" width="161px">
@@ -26,4 +26,7 @@
<a href="src/day8.rs">
<img src=".aoc_tiles/tiles/2025/08.png" width="161px">
</a>
<a href="src/day9.rs">
<img src=".aoc_tiles/tiles/2025/09.png" width="161px">
</a>
<!-- AOC TILES END -->

View File

@@ -9,13 +9,13 @@ struct Junction {
}
fn squared_distance(a: &Junction, b: &Junction) -> u64 {
if a.pos == b.pos {
0
} else {
// if a.pos == b.pos {
// 0
// } else {
(a.pos.0 - b.pos.0).pow(2) as u64
+ (a.pos.1 - b.pos.1).pow(2) as u64
+ (a.pos.2 - b.pos.2).pow(2) as u64
}
// }
}
impl Junction {
@@ -144,6 +144,7 @@ struct JunctionPair {
a: usize,
b: usize,
}
fn make_heap(circuits: &Circuits) -> BinaryHeap<Reverse<JunctionPair>> {
BinaryHeap::from_iter(
circuits

141
src/day9.rs Normal file
View File

@@ -0,0 +1,141 @@
use aoc_runner_derive::{aoc, aoc_generator};
use grid::{AsCoord2d, Coord2d, Grid};
use itertools::Itertools;
use std::cmp::{max, min};
#[aoc_generator(day9, part1)]
fn parse(input: &str) -> Vec<Coord2d> {
input.lines().map(|l| l.parse().unwrap()).collect()
}
#[aoc(day9, part1)]
fn part1(input: &Vec<Coord2d>) -> u64 {
input
.iter()
.tuple_combinations()
.inspect(|(a, b)| {
println!(
"{a} vs {b} = {}",
a.x().abs_diff(b.x()) * a.y().abs_diff(b.y())
)
})
.map(|(a, b)| (a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1))
.sorted_unstable()
.next_back()
.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;
let mut grid = Grid::with_shape(width as usize, height as usize, b'.');
let mut prev = reds.last().unwrap();
for c in &reds {
// mark c filled
grid.set(&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.set(&(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.set(&(x, c.y()), b'X');
}
} else {
panic!()
}
prev = c
}
println!("grid {}x{}", grid.width(), grid.height());
(reds, grid)
}
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 {
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)
}
}
Some(b'#') | Some(b'X') => {
state = State::OnLine(match state {
State::OnLine(s) | State::OffLine(s) => s,
})
}
None => panic!("overran the grid"),
Some(c) => panic!("unexpected value {c}"),
}
}
}
}
#[aoc(day9, part2)]
fn part2((reds, grid): &(Vec<Coord2d>, Grid<u8>)) -> u64 {
let mut grid = grid.clone();
flood_fill(&mut grid);
'outer: for (a, b) in 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())
);
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 (a.x().abs_diff(b.x()) + 1) * (a.y().abs_diff(b.y()) + 1);
}
panic!()
}
#[cfg(test)]
mod tests {
use super::*;
const EXAMPLE: &str = "7,1
11,1
11,7
9,7
9,5
2,5
2,3
7,3";
#[test]
fn part1_example() {
assert_eq!(part1(&parse(EXAMPLE)), 50);
}
#[test]
fn part2_example() {
assert_eq!(part2(&parse2(EXAMPLE)), 24);
}
}

View File

@@ -6,6 +6,7 @@ mod day5;
mod day6;
mod day7;
mod day8;
mod day9;
use aoc_runner_derive::aoc_lib;

View File

@@ -22,22 +22,37 @@ pub const ADJACENT_OFFSETS: [&(i64, i64); 8] = [
/// NESW
pub const CARDINAL_OFFSETS: [&(i64, i64); 4] = [&(0, -1), &(-1, 0), &(1, 0), &(0, 1)];
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Coord2d {
pub x: i64,
pub y: i64,
}
impl Debug for Coord2d {
impl Display for Coord2d {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("({}, {})", self.x, self.y))
}
}
impl FromStr for Coord2d {
type Err = Box<dyn std::error::Error>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (l, r) = s.split_once(',').ok_or("Can't split on ,")?;
Ok(Coord2d {
x: l.parse()?,
y: r.parse()?,
})
}
}
pub trait AsCoord2d {
fn to_coord(self) -> Coord2d;
fn x(&self) -> i64;
fn y(&self) -> i64;
fn manhattan<T: AsCoord2d>(&self, other: &T) -> u64 {
self.x().abs_diff(other.x()) + self.y().abs_diff(other.y())
}
}
impl<T: AsCoord2d> Sub<T> for &Coord2d {