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); } }