1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
/* Copyright 2023 Mario Finelli
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! Advent of Code 2023 Day 11: <https://adventofcode.com/2023/day/11>
//!
//! I originally wasn't sure how to handle this problem, and I considered
//! building a large grid to hold all of the galaxies (and the empty spaces)
//! but I was stuck on how to properly expand it for the empty rows/columns.
//! Part two would have then probably made the memory requirements outrageous
//! adding millions and millions of entries. Inspiration from the Advent of
//! Code subreddit instead led me down the path of instead just finding the
//! empty rows and columns and then adding them to the distance instead of
//! expanding the grid (or trying to modify the galaxy coordinates) and then
//! computing the Manhattan distance afterwards.
use itertools::Itertools;
/// The solution for the day eleven challenge.
///
/// We start with the input as a string and then the second parameter is for
/// how much the empty spaces are supposed to expand (which allows us to easily
/// test the answers given in the prompt's example as opposed to the usual
/// part one or two). We start by parsing the input to find the coordinates of
/// all of the galaxies. We also initialize vectors for the empty rows and
/// columns and fill them with all of the rows and columns to start. After
/// finding all of the galaxies we do a single pass over them to eliminate
/// their x and y coordinates from the lists of empty columns and rows. Then,
/// we generate the combinations for each galaxy to every other galaxy and
/// calculate the Manhattan distance. Then we generate ranges for the x any
/// y coordinates from one galaxy to the other and check to see if we pass over
/// any of the empty rows or columns. For each that we pass over we add our
/// modifier parameter. Then, we add the distance to our running sum, which we
/// return after processing all of the galaxies.
///
/// # Example
/// ```rust
/// # use aoc::y23d11::y23d11;
/// // probably read this from the input file...
/// let input = ".#...\n.....\n..#.#\n..#..\n#....";
/// assert_eq!(y23d11(input, 50), 428);
/// ```
pub fn y23d11(input: &str, modifier: i64) -> i64 {
let mut galaxies: Vec<(i64, i64)> = Vec::new();
let mut empty_rows: Vec<i64> = Vec::new();
let mut empty_cols: Vec<i64> = Vec::new();
let mut sum = 0;
let modifier = if modifier == 1 { 1 } else { modifier - 1 };
for (y, line) in input.lines().enumerate() {
empty_rows.push(y.try_into().unwrap());
for (x, c) in line.chars().enumerate() {
if y == 0 {
empty_cols.push(x.try_into().unwrap());
}
if c == '#' {
galaxies.push((x.try_into().unwrap(), y.try_into().unwrap()));
}
}
}
for galaxy in &galaxies {
let (x, y) = galaxy;
empty_rows.retain(|r| r != y);
empty_cols.retain(|c| c != x);
}
for combination in galaxies.iter().combinations(2) {
let (ax, ay) = combination[0];
let (bx, by) = combination[1];
let mut md = (ax - bx).abs() + (ay - by).abs();
let xrange = if ax <= bx { ax..bx } else { bx..ax };
let yrange = if ay <= by { ay..by } else { by..ay };
for row in &empty_rows {
if yrange.contains(&row) {
md += modifier;
}
}
for col in &empty_cols {
if xrange.contains(&col) {
md += modifier;
}
}
sum += md;
}
sum
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn it_works() {
let input = concat!(
"...#......\n",
".......#..\n",
"#.........\n",
"..........\n",
"......#...\n",
".#........\n",
".........#\n",
"..........\n",
".......#..\n",
"#...#.....\n",
);
assert_eq!(y23d11(input, 1), 374);
assert_eq!(y23d11(input, 10), 1030);
assert_eq!(y23d11(input, 100), 8410);
}
#[test]
fn the_solution() {
let contents = fs::read_to_string("input/2023/day11.txt").unwrap();
assert_eq!(y23d11(&contents, 1), 9608724);
assert_eq!(y23d11(&contents, 1000000), 904633799472);
}
}