diff --git a/.aoc_tiles/tiles/2025/09.png b/.aoc_tiles/tiles/2025/09.png
new file mode 100644
index 0000000..32e6986
Binary files /dev/null and b/.aoc_tiles/tiles/2025/09.png differ
diff --git a/README.md b/README.md
index 7c943c9..b97cb02 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
- 2025 - 16 ⭐ - Rust
+ 2025 - 17 ⭐ - Rust
@@ -26,4 +26,7 @@
+
+
+
diff --git a/src/day8.rs b/src/day8.rs
index 0f8c360..de7e153 100644
--- a/src/day8.rs
+++ b/src/day8.rs
@@ -9,13 +9,13 @@ struct Junction {
}
fn squared_distance(a: &Junction, b: &Junction) -> u64 {
- 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
- }
+ // 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> {
BinaryHeap::from_iter(
circuits
diff --git a/src/day9.rs b/src/day9.rs
new file mode 100644
index 0000000..20923c5
--- /dev/null
+++ b/src/day9.rs
@@ -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 {
+ input.lines().map(|l| l.parse().unwrap()).collect()
+}
+
+#[aoc(day9, part1)]
+fn part1(input: &Vec) -> 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, Grid) {
+ 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) {
+ #[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, Grid)) -> 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);
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index ac83a67..b38dc0b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,6 +6,7 @@ mod day5;
mod day6;
mod day7;
mod day8;
+mod day9;
use aoc_runner_derive::aoc_lib;
diff --git a/utils/grid/lib.rs b/utils/grid/lib.rs
index f2fa866..6e60bc5 100644
--- a/utils/grid/lib.rs
+++ b/utils/grid/lib.rs
@@ -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;
+ fn from_str(s: &str) -> Result {
+ 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(&self, other: &T) -> u64 {
+ self.x().abs_diff(other.x()) + self.y().abs_diff(other.y())
+ }
}
impl Sub for &Coord2d {