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 {