diff --git a/2022/rust/src/solutions/day15.rs b/2022/rust/src/solutions/day15.rs new file mode 100644 index 0000000..64ebfef --- /dev/null +++ b/2022/rust/src/solutions/day15.rs @@ -0,0 +1,137 @@ +use std::collections::HashMap; +use regex::Regex; +use crate::utils; +use crate::utils::matrix::Matrix; + + +pub fn run() -> () { + let data = parse_data(&utils::read_lines(utils::Source::Day(-1))); + + println!("Day 15"); + println!("Part 1: {}", solve1(&data)); + println!("Part 2: {}", solve2(&data)); +} + + +type Data = [((usize, usize), (isize, isize))]; + + +fn distance(a: (usize, usize), b: (isize, isize)) -> usize { + (a.0 as isize).abs_diff(b.0) + (a.1 as isize).abs_diff(b.1) +} + +fn find_ranges(data: &Data) -> HashMap<(usize, usize), usize> { + data.iter() + .map(|&(sensor, beacon)| + (sensor, distance(sensor, beacon)) + ) + .collect() +} + + +fn find_bounds(data: &Data) -> (usize, usize) { + data.iter() + .fold((0, 0), |acc, &(sensor, beacon)| { + ( + acc.0.max(sensor.0.max(beacon.0.max(0) as usize)), + acc.1.max(sensor.0.max(beacon.1.max(0) as usize)), + ) + }) +} + +fn solve1(data: &Data) -> usize { + let ranges = find_ranges(data); + let (rows, cols) = find_bounds(data); + let max_range = *ranges.values().max().unwrap(); + let buffer = 2 * max_range; + + let mut grid = Matrix::with_bounds( + (0, 0), + (rows + buffer, cols + buffer), + '.' + ); + + let mut view = grid.view_translated((-(max_range as isize), -(max_range as isize))); + + for index in view.cell_indices().collect::>() { + for (&sensor, &max_dist) in ranges.iter() { + if distance(sensor, index) <= max_dist { + view[index] = '#'; + } + } + } + + grid.iter_rows().for_each(|row| { + let xd = row.iter().filter(|&&x| x == '#').count(); + println!("{xd}"); + }); + + println!("{grid}"); + + grid.slice_row(max_range + 10).iter().filter(|&&x| x == '#').count() +} + +fn solve2(data: &Data) -> i32 { + 2 +} + + +fn parse_data>(data: &[T]) -> Vec<((usize, usize), (isize, isize))> { + let re = Regex::new(r#"x=(-?\d+), y=(-?\d+)"#).unwrap(); + data.iter() + .map(|line| { + let mut iter = re.captures_iter(line.as_ref()); + let sensor = iter.next() + .map(|c| { + let mut cap = c.iter(); + let x = cap.nth(1).unwrap().unwrap().as_str().parse::().unwrap(); + let y = cap.next().unwrap().unwrap().as_str().parse::().unwrap(); + (y, x) + }) + .unwrap(); + let beacon = iter.next() + .map(|c| { + let mut cap = c.iter(); + let x = cap.nth(1).unwrap().unwrap().as_str().parse::().unwrap(); + let y = cap.next().unwrap().unwrap().as_str().parse::().unwrap(); + (y, x) + }) + .unwrap(); + (sensor, beacon) + }) + .collect() +} + + +#[cfg(test)] +mod tests { + use super::*; + + static DATA: &[&str] = &[ + // "Sensor at x=2, y=18: closest beacon is at x=-2, y=15", + // "Sensor at x=9, y=16: closest beacon is at x=10, y=16", + // "Sensor at x=13, y=2: closest beacon is at x=15, y=3", + // "Sensor at x=12, y=14: closest beacon is at x=10, y=16", + // "Sensor at x=10, y=20: closest beacon is at x=10, y=16", + // "Sensor at x=14, y=17: closest beacon is at x=10, y=16", + "Sensor at x=8, y=7: closest beacon is at x=2, y=10", + // "Sensor at x=2, y=0: closest beacon is at x=2, y=10", + // "Sensor at x=0, y=11: closest beacon is at x=2, y=10", + // "Sensor at x=20, y=14: closest beacon is at x=25, y=17", + // "Sensor at x=17, y=20: closest beacon is at x=21, y=22", + // "Sensor at x=16, y=7: closest beacon is at x=15, y=3", + // "Sensor at x=14, y=3: closest beacon is at x=15, y=3", + // "Sensor at x=20, y=1: closest beacon is at x=15, y=3", + ]; + + #[test] + fn part1() { + assert_eq!(26, solve1(&parse_data(DATA))); + } + + #[test] + fn part2() { + let data = parse_data(DATA); + assert_eq!(2, solve2(&data)); + } +} diff --git a/2022/rust/src/solutions/mod.rs b/2022/rust/src/solutions/mod.rs index f4877db..c4cadc0 100644 --- a/2022/rust/src/solutions/mod.rs +++ b/2022/rust/src/solutions/mod.rs @@ -12,3 +12,4 @@ pub mod day11; pub mod day12; pub mod day13; pub mod day14; +pub mod day15; diff --git a/2022/rust/src/utils/matrix.rs b/2022/rust/src/utils/matrix.rs index 1958a33..c1f8ca7 100644 --- a/2022/rust/src/utils/matrix.rs +++ b/2022/rust/src/utils/matrix.rs @@ -85,6 +85,12 @@ impl Matrix { .flat_map(move |row| col_range.clone().into_iter().map(move |col| (row, col))) } + pub fn slice_row(&self, row: usize) -> &[T] { + let start = row * self.cols; + let end = start + self.cols; + &self.array[start..end] + } + #[inline(always)] fn get_offset(&self, row: usize, col: usize) -> usize { (row - self.offset_r) * self.cols + (col - self.offset_c) @@ -155,3 +161,54 @@ impl fmt::Display for Matrix where T: fmt::Display { } } + +pub struct TranslatedViewMut<'a, T> { + matrix: &'a mut Matrix, + tl_row: isize, + tl_col: isize, +} + +impl Matrix { + pub fn view_translated(&mut self, (rows, cols): (isize, isize)) -> TranslatedViewMut<'_, T> { + TranslatedViewMut { matrix: self, tl_row: rows, tl_col: cols } + } +} + +impl<'a, T> TranslatedViewMut<'a, T> { + fn tl(&self, (row, col): (usize, usize)) -> (isize, isize) { + let row = row as isize + self.tl_row; + let col = col as isize + self.tl_col; + (row, col) + } + + fn undo_tl(&self, (row, col): (isize, isize)) -> (usize, usize) { + let row = (row - self.tl_row) as usize; + let col = (col - self.tl_col) as usize; + (row, col) + } + + pub fn cell_indices(&self) -> impl Iterator + '_ { + self.matrix.cell_indices().map(|index| self.tl(index)) + } + + pub fn slice_row(&self, row: isize) -> &[T] { + let row = row - self.tl_row; + self.slice_row(row) + } +} + +impl<'a, T> Index<(isize, isize)> for TranslatedViewMut<'a, T> { + type Output = T; + + fn index(&self, index: (isize, isize)) -> &Self::Output { + let index = self.undo_tl(index); + &self.matrix[index] + } +} + +impl<'a, T> IndexMut<(isize, isize)> for TranslatedViewMut<'a, T> { + fn index_mut(&mut self, index: (isize, isize)) -> &mut Self::Output { + let index = self.undo_tl(index); + &mut self.matrix[index] + } +}