Skip to content

Commit

Permalink
feat: 2023 day 11 complete
Browse files Browse the repository at this point in the history
  • Loading branch information
GriceTurrble committed Dec 12, 2023
1 parent fd373bb commit ef198c4
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 52 deletions.
1 change: 1 addition & 0 deletions 2023/aoc2023/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions 2023/aoc2023/day11/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ description.workspace = true

[dependencies]
lube = { path = "../lube" }
itertools = { workspace = true }
14 changes: 12 additions & 2 deletions 2023/aoc2023/day11/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,18 @@ Link: <https://adventofcode.com/2023/day/11>

## Part 1

*TBD*
First and foremost we need a method for "expanding the universe", which is *probably* best done through just some mathematical equation after identifying those rows and columns, rather than actually adjusting the input grid. But where's the fun in that?

It's a simple matter of `.clone()`ing and `.push()`ing something onto an existing vec, so handling rows is simple. Columns are less simple, of course, but we don't need to worry about them much: just transpose the grid and then perform the same push operation on the new "rows".

After that, search for all galaxies, determining their positions in the grid; then use [`itertools.combination`](https://docs.rs/itertools/latest/itertools/trait.Itertools.html#method.combinations) to produce the various points we need to worry about. From there it's a simple matter of calculating their distances (essentially `abs(x1 - x2) + abs(y1 - y2)`), and then summing that value.

## Part 2

*TBD*
Sure enough, performing the whole insert and transpose ain't gonna cut it: we can't go shoving 1_000_000 rows in every time we find something. We'd quickly run out of memory!

Instead, we'll need to identify those rows and cols separately as being empty. I think then we can test if the range between, say, `x1..x2` includes one or more of those rows. If so, we add `999_999` (accounting for an off-by-one issue) to the distance calculation manually.

Interestingly, the test cases use `n` of `10` and `100` to demonstrate different values. This leads us to write the func taking an argument; one which could be used to solve the first part in the same way.

So, while the original did include a `part1.rs` and `part2.rs`, I opted to instead use a `funcs` module to move pieces out of the main module (trying to increase some clarity by reducing the lines of code per file). Then we move the solution from part 2 back to `main.rs`, rename to `fn solve()`, and add the `expansion: u64` variable (in place of `n`). Part 1 is now solved by the same method, and tests are run against all 3 examples.
140 changes: 140 additions & 0 deletions 2023/aoc2023/day11/input.txt

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions 2023/aoc2023/day11/src/funcs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
pub fn transpose_grid(contents: Vec<String>) -> Vec<String> {
let thing: Vec<Vec<char>> = contents
.iter()
.map(|line| line.chars().collect::<Vec<char>>())
.collect();
let transposed = transpose(thing);
transposed
.iter()
.map(|inner| inner.into_iter().collect::<String>())
.collect()
}

fn transpose<T>(v: Vec<Vec<T>>) -> Vec<Vec<T>>
where
T: Clone,
{
assert!(!v.is_empty());
(0..v[0].len())
.map(|i| v.iter().map(|inner| inner[i].clone()).collect::<Vec<T>>())
.collect()
}

pub fn find_galaxies(contents: Vec<String>) -> Vec<(usize, usize)> {
let mut output: Vec<(usize, usize)> = Vec::new();
for (row, line) in contents.iter().enumerate() {
for (col, char) in line.chars().into_iter().enumerate() {
if char == '#' {
output.push((row, col));
}
}
}
output
}

pub fn find_empty_rows(grid: &Vec<String>) -> Vec<usize> {
let mut result: Vec<usize> = Vec::new();
for (idx, row) in grid.iter().enumerate() {
if !row.contains('#') {
result.push(idx);
}
}
result
}
81 changes: 77 additions & 4 deletions 2023/aoc2023/day11/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#![doc = include_str!("../README.md")]
use std::cmp;
use std::time::Instant;
use itertools::Itertools;

use funcs::{find_empty_rows, find_galaxies, transpose_grid};
use lube::{get_file_contents, get_input_file_path};

mod part1;
mod part2;
mod funcs;

fn main() {
let inp_file_path: std::path::PathBuf = get_input_file_path();
Expand All @@ -13,13 +15,84 @@ fn main() {

println!("-------------------- PART 1 --------------------");
let part1start = Instant::now();
let result = part1::solution(&contents);
let result = solve(&contents, 2);
println!(">> {result}");
println!(" [{} μs]", part1start.elapsed().as_micros());

println!("-------------------- PART 2 --------------------");
let part2start = Instant::now();
let result = part2::solution(&contents);
let result = solve(&contents, 1_000_000);
println!(">> {result}");
println!(" [{} μs]", part2start.elapsed().as_micros());
}

fn solve(contents: &Vec<&str>, expansion: u64) -> u64 {
let grid: Vec<String> = contents.iter().map(|s| s.to_string()).collect();
let empty_rows = find_empty_rows(&grid);
let transposed = transpose_grid(grid);
let empty_cols = find_empty_rows(&transposed);
let galaxies = find_galaxies(transposed);

let sum_distances: u64 = galaxies
.iter()
.combinations(2)
.map(|combo| {
let found_cols = empty_cols
.iter()
.filter(|&&c| {
let (x1, x2) = (combo[0].0, combo[1].0);
let lower = cmp::min(x1, x2);
let upper = cmp::max(x1, x2);
(lower..upper).contains(&c)
})
.count() as u64;
let found_rows = empty_rows
.iter()
.filter(|&&c| {
let (y1, y2) = (combo[0].1, combo[1].1);
let lower = cmp::min(y1, y2);
let upper = cmp::max(y1, y2);
(lower..upper).contains(&c)
})
.count() as u64;

let absx = (combo[0].0 as i64 - combo[1].0 as i64).abs() as u64;
let absx = absx + ((expansion - 1) * found_rows);

let absy = (combo[0].1 as i64 - combo[1].1 as i64).abs() as u64;
let absy = absy + ((expansion - 1) * found_cols);

absx + absy
})
.sum();

sum_distances
}

#[cfg(test)]
mod tests {
use super::*;

fn get_sample_data() -> Vec<&'static str> {
let data = "...#......\n.......#..\n#.........\n..........\n......#...\n.#........\n.........#\n..........\n.......#..\n#...#.....";
data.trim().split("\n").collect()
}

fn dotest(contents: &Vec<&str>, expansion: u64, expected: u64) {
let result = solve(&contents, expansion);
assert_eq!(result, expected);
}

#[test]
fn test_part1() {
let data = get_sample_data();
dotest(&data, 2, 374);
}

#[test]
fn test_part2() {
let data = get_sample_data();
dotest(&data, 10, 1030);
dotest(&data, 100, 8410);
}
}
23 changes: 0 additions & 23 deletions 2023/aoc2023/day11/src/part1.rs

This file was deleted.

23 changes: 0 additions & 23 deletions 2023/aoc2023/day11/src/part2.rs

This file was deleted.

0 comments on commit ef198c4

Please sign in to comment.