From 14c6da72d94f4ad80375cd0dd4489f0de40ef63b Mon Sep 17 00:00:00 2001 From: Andrew Glaze Date: Wed, 27 Dec 2023 10:20:20 -0500 Subject: [PATCH] day24 --- src/day24.rs | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 145 insertions(+) create mode 100644 src/day24.rs diff --git a/src/day24.rs b/src/day24.rs new file mode 100644 index 0000000..4d4277b --- /dev/null +++ b/src/day24.rs @@ -0,0 +1,144 @@ +use std::ops::RangeInclusive; + +use aoc_runner_derive::{aoc, aoc_generator}; + +//const RANGE: RangeInclusive = 7.0..=27.0; +const RANGE: RangeInclusive = 200_000_000_000_000.0..=400_000_000_000_000.0; + +#[aoc_generator(day24)] +fn parse(input: &str) -> Vec<[f64; 6]> { + let input: Vec<[f64; 6]> = input.lines() + .map(|line| { + line.split("@") + .flat_map(|components| { + components.split(",") + .map(str::trim) + .map(|num| num.parse::().unwrap()) + }) + .collect::>().try_into().unwrap() + }).collect::>(); + + input +} + +#[aoc(day24, part1)] +fn part1(input: &[[f64; 6]]) -> u32 { + let mut total = 0; + + // It's just solve the linear equations. I'm using a matrix. + for i in 1..input.len() { + for j in 0..i { + let [a, b, _, d, e, _] = input[i]; + let [g, h, _, j, k, _] = input[j]; + + let determinant = e * j - d * k; + if determinant == 0.0 { + continue; + } + + let t = (j * (h - b) - k * (g - a)) / determinant; + let u = (d * (h - b) - e * (g - a)) / determinant; + + let x = a + t * d; + let y = b + t * e; + + if t >= 0.0 && u >= 0.0 && RANGE.contains(&x) && RANGE.contains(&y) { + total += 1; + } + } + } + + total +} + +#[aoc(day24, part2)] +fn part2(input: &[[f64; 6]]) -> f64 { + let [a, b, c, d, e, f] = input[0]; + let [g, h, i, j, k, l] = input[1]; + let [m, n, o, p, q, r] = input[2]; + + let mut matrix = [ + [0.0, l - f, e - k, 0.0, c - i, h - b, e * c - b * f + h * l - k * i], + [0.0, r - f, e - q, 0.0, c - o, n - b, e * c - b * f + n * r - q * o], + [f - l, 0.0, j - d, i - c, 0.0, a - g, a * f - d * c + j * i - g * l], + [f - r, 0.0, p - d, o - c, 0.0, a - m, a * f - d * c + p * o - m * r], + [k - e, d - j, 0.0, b - h, g - a, 0.0, d * b - a * e + g * k - j * h], + [q - e, d - p, 0.0, b - n, m - a, 0.0, d * b - a * e + m * q - p * n], + ]; + + // Use Gaussian elimination to solve for the 6 unknowns. + // Forward elimination, processing columns from left to right. + // This will leave a matrix in row echelon form. + // That's right, I got a D in applied linear algebra. + for pivot in 0..6 { + // Make leading coefficient of each row positive to make subsequent calculations easier. + for row in &mut matrix[pivot..] { + if row[pivot] < 0.0 { + row.iter_mut().for_each(|num| *num = -*num); + } + } + + loop { + let column = matrix.map(|row| row[pivot]); + + // If only one non-zero coefficient remaining in the column then we're done. + if column[pivot..].iter().filter(|&&c| c > 0.0).count() == 1 { + // Move this row into the pivot location + let index = column.iter().rposition(|&c| c > 0.0).unwrap(); + matrix.swap(pivot, index); + break; + } + + // Find the row with the lowest non-zero leading coefficient. + let min = *column[pivot..].iter().filter(|&&c| c > 0.0).min_by(|a, b| a.total_cmp(b)).unwrap(); + let index = column.iter().rposition(|&c| c == min).unwrap(); + + // Subtract as many multiples of this minimum row from each other row as possible + // to shrink the coefficients of our column towards zero. + for row in pivot..6 { + if row != index && column[row] != 0.0 { + let factor = column[row] / min; + + for col in pivot..7 { + matrix[row][col] -= factor * matrix[index][col]; + } + } + } + } + } + + // Back substitution, processing columns from right to left. + // This will leave the matrix in reduced row echelon form. + // The solved unknowns are then in the 7th column. + for pivot in (0..6).rev() { + matrix[pivot][6] /= matrix[pivot][pivot]; + + for row in 0..pivot { + matrix[row][6] -= matrix[pivot][6] * matrix[row][pivot]; + } + } + + (matrix[0][6] + matrix[1][6] + matrix[2][6]).floor() +} + + +#[cfg(test)] +mod tests { + use super::*; + + const EX: &str = r"19, 13, 30 @ -2, 1, -2 +18, 19, 22 @ -1, -1, -2 +20, 25, 34 @ -2, -2, -4 +12, 31, 28 @ -1, -2, -1 +20, 19, 15 @ 1, -5, -3"; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EX)), 2); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX)), 47.0); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index bbefed4..b12b494 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod day24; mod day23; mod day22; mod day21;