generated from luke-else/AdventOfCodeXXXX
feat: Completed day 8 and fixed up clippy warnings in code.
All checks were successful
Continuous integration / Check (push) Successful in 38s
Continuous integration / Test Suite (push) Successful in 41s
Continuous integration / Rustfmt (push) Successful in 29s
Continuous integration / Clippy (push) Successful in 41s
Continuous integration / build (push) Successful in 40s
All checks were successful
Continuous integration / Check (push) Successful in 38s
Continuous integration / Test Suite (push) Successful in 41s
Continuous integration / Rustfmt (push) Successful in 29s
Continuous integration / Clippy (push) Successful in 41s
Continuous integration / build (push) Successful in 40s
This commit is contained in:
1000
input/day08
1000
input/day08
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
|||||||
|
162,817,812
|
||||||
|
57,618,57
|
||||||
|
906,360,560
|
||||||
|
592,479,940
|
||||||
|
352,342,300
|
||||||
|
466,668,158
|
||||||
|
542,29,236
|
||||||
|
431,825,988
|
||||||
|
739,650,466
|
||||||
|
52,470,668
|
||||||
|
216,146,977
|
||||||
|
819,987,18
|
||||||
|
117,168,530
|
||||||
|
805,96,715
|
||||||
|
346,949,466
|
||||||
|
970,615,88
|
||||||
|
941,993,340
|
||||||
|
862,61,35
|
||||||
|
984,92,344
|
||||||
|
425,690,689
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
162,817,812
|
||||||
|
57,618,57
|
||||||
|
906,360,560
|
||||||
|
592,479,940
|
||||||
|
352,342,300
|
||||||
|
466,668,158
|
||||||
|
542,29,236
|
||||||
|
431,825,988
|
||||||
|
739,650,466
|
||||||
|
52,470,668
|
||||||
|
216,146,977
|
||||||
|
819,987,18
|
||||||
|
117,168,530
|
||||||
|
805,96,715
|
||||||
|
346,949,466
|
||||||
|
970,615,88
|
||||||
|
941,993,340
|
||||||
|
862,61,35
|
||||||
|
984,92,344
|
||||||
|
425,690,689
|
||||||
@@ -19,7 +19,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
Box::new(day05::Day05 {}),
|
Box::new(day05::Day05 {}),
|
||||||
Box::new(day06::Day06 {}),
|
Box::new(day06::Day06 {}),
|
||||||
Box::new(day07::Day07 {}),
|
Box::new(day07::Day07 {}),
|
||||||
// Box::new(day08::Day08 {}),
|
Box::new(day08::Day08 {}),
|
||||||
// Box::new(day09::Day09 {}),
|
// Box::new(day09::Day09 {}),
|
||||||
// Box::new(day10::Day10 {}),
|
// Box::new(day10::Day10 {}),
|
||||||
// Box::new(day11::Day11 {}),
|
// Box::new(day11::Day11 {}),
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ impl Solution for Day06 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(op) = op {
|
if let Some(op) = op
|
||||||
if !numbers.is_empty() {
|
&& !numbers.is_empty()
|
||||||
final_problems.push((numbers, op));
|
{
|
||||||
}
|
final_problems.push((numbers, op));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,11 +78,8 @@ impl Solution for Day06 {
|
|||||||
|
|
||||||
let mut problems: Vec<Vec<char>> = Vec::new();
|
let mut problems: Vec<Vec<char>> = Vec::new();
|
||||||
for col in 0..width {
|
for col in 0..width {
|
||||||
let mut column_chars = Vec::new();
|
let column: Vec<char> = grid.iter().map(|row| row[col]).collect();
|
||||||
for row in 0..height {
|
problems.push(column);
|
||||||
column_chars.push(grid[row][col]);
|
|
||||||
}
|
|
||||||
problems.push(column_chars);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split columns into groups separated by all-space columns
|
// Split columns into groups separated by all-space columns
|
||||||
@@ -116,7 +113,7 @@ impl Solution for Day06 {
|
|||||||
let mut num_str = String::new();
|
let mut num_str = String::new();
|
||||||
for &c in &col[..height - 1] {
|
for &c in &col[..height - 1] {
|
||||||
// all but the last row
|
// all but the last row
|
||||||
if c.is_digit(10) {
|
if c.is_ascii_digit() {
|
||||||
num_str.push(c);
|
num_str.push(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ impl Solution for Day07 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Day07 {
|
impl Day07 {
|
||||||
fn solve_paths(&self, input: &Vec<String>) -> (usize, usize) {
|
fn solve_paths(&self, input: &[String]) -> (usize, usize) {
|
||||||
let lines: Vec<_> = input.iter().map(String::as_bytes).collect();
|
let lines: Vec<_> = input.iter().map(String::as_bytes).collect();
|
||||||
let width = lines[0].len();
|
let width = lines[0].len();
|
||||||
let start = lines[0].iter().position(|&b| b == b'S').unwrap();
|
let start = lines[0].iter().position(|&b| b == b'S').unwrap();
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
use std::cmp::Reverse;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
use super::Solution;
|
use super::Solution;
|
||||||
|
|
||||||
pub struct Day08 {}
|
pub struct Day08 {}
|
||||||
@@ -5,16 +8,30 @@ pub struct Day08 {}
|
|||||||
impl Solution for Day08 {
|
impl Solution for Day08 {
|
||||||
fn part1(
|
fn part1(
|
||||||
&self,
|
&self,
|
||||||
_input: &mut Vec<String>,
|
input: &mut Vec<String>,
|
||||||
) -> Result<Box<dyn std::fmt::Display>, Box<dyn std::error::Error>> {
|
) -> Result<Box<dyn std::fmt::Display>, Box<dyn std::error::Error>> {
|
||||||
Ok(Box::new("Ready"))
|
let points = self.parse_points(input)?;
|
||||||
|
if points.is_empty() {
|
||||||
|
eprintln!("No points provided.");
|
||||||
|
return Err("no points".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// default k = 1000 for the puzzle, but allow override with env var or small change here
|
||||||
|
let k = 1000usize; // for test input
|
||||||
|
Ok(Box::new(self.solve_part1(&points, k)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(
|
fn part2(
|
||||||
&self,
|
&self,
|
||||||
_input: &mut Vec<String>,
|
input: &mut Vec<String>,
|
||||||
) -> Result<Box<dyn std::fmt::Display>, Box<dyn std::error::Error>> {
|
) -> Result<Box<dyn std::fmt::Display>, Box<dyn std::error::Error>> {
|
||||||
Ok(Box::new("Ready"))
|
let points = self.parse_points(input)?;
|
||||||
|
if points.is_empty() {
|
||||||
|
eprintln!("No points provided.");
|
||||||
|
return Err("no points".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Box::new(self.solve_part2(&points)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_day(&self) -> u8 {
|
fn get_day(&self) -> u8 {
|
||||||
@@ -22,7 +39,197 @@ impl Solution for Day08 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Day08 {}
|
impl Day08 {
|
||||||
|
/// Build all pairwise edges from points (upper triangle).
|
||||||
|
fn build_edges(&self, points: &[Point]) -> Vec<Edge> {
|
||||||
|
let n = points.len();
|
||||||
|
let mut edges = Vec::with_capacity(n.saturating_mul(n.saturating_sub(1)) / 2);
|
||||||
|
for i in 0..n {
|
||||||
|
for j in (i + 1)..n {
|
||||||
|
edges.push(Edge {
|
||||||
|
a: i,
|
||||||
|
b: j,
|
||||||
|
dist2: points[i].dist2(&points[j]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
edges
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Solve: take first k edges sorted by distance, union them, then multiply top 3 component sizes.
|
||||||
|
fn solve_part1(&self, points: &[Point], k: usize) -> u128 {
|
||||||
|
let n = points.len();
|
||||||
|
if n == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let mut edges = self.build_edges(points);
|
||||||
|
// sort ascending by distance (stable)
|
||||||
|
edges.sort_by_key(|e| e.dist2);
|
||||||
|
|
||||||
|
let mut uf = UnionFind::new(n);
|
||||||
|
let take = k.min(edges.len());
|
||||||
|
for e in edges.into_iter().take(take) {
|
||||||
|
uf.union(e.a, e.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sizes = uf.sizes();
|
||||||
|
sizes.sort_by_key(|&s| Reverse(s));
|
||||||
|
// multiply top 3 (or fewer if less)
|
||||||
|
let mut prod: u128 = 1;
|
||||||
|
for s in sizes.iter().take(3) {
|
||||||
|
prod = prod.saturating_mul(*s as u128);
|
||||||
|
}
|
||||||
|
prod
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Part Two:
|
||||||
|
/// walk through edges; when union_count == n-1, return X_a * X_b of that last union.
|
||||||
|
fn solve_part2(&self, points: &[Point]) -> i128 {
|
||||||
|
let n = points.len();
|
||||||
|
if n <= 1 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut edges = self.build_edges(points);
|
||||||
|
edges.sort_by_key(|e| e.dist2);
|
||||||
|
|
||||||
|
let mut uf = UnionFind::new(n);
|
||||||
|
let mut unions = 0usize;
|
||||||
|
let target = n - 1;
|
||||||
|
|
||||||
|
for e in edges {
|
||||||
|
if uf.union(e.a, e.b) {
|
||||||
|
unions += 1;
|
||||||
|
if unions == target {
|
||||||
|
let xa = points[e.a].x as i128;
|
||||||
|
let xb = points[e.b].x as i128;
|
||||||
|
return xa * xb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// should never happen unless input is malformed
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse many lines of "x,y,z".
|
||||||
|
fn parse_points(&self, input: &[String]) -> Result<Vec<Point>, Box<dyn Error>> {
|
||||||
|
let mut points = Vec::new();
|
||||||
|
for (idx, line) in input.iter().enumerate() {
|
||||||
|
let line = line.trim();
|
||||||
|
if line.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match Point::from_csv_line(line) {
|
||||||
|
Ok(p) => points.push(p),
|
||||||
|
Err(e) => return Err(format!("line {}: {}", idx + 1, e).into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(points)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A 3D point with integer coords.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Point {
|
||||||
|
x: i64,
|
||||||
|
y: i64,
|
||||||
|
z: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Point {
|
||||||
|
fn from_csv_line(s: &str) -> Result<Self, Box<dyn Error>> {
|
||||||
|
let parts: Vec<_> = s.trim().split(',').collect();
|
||||||
|
if parts.len() != 3 {
|
||||||
|
return Err(format!("invalid point line: {}", s).into());
|
||||||
|
}
|
||||||
|
let x = parts[0].parse::<i64>()?;
|
||||||
|
let y = parts[1].parse::<i64>()?;
|
||||||
|
let z = parts[2].parse::<i64>()?;
|
||||||
|
Ok(Point { x, y, z })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// squared Euclidean distance, as u128 to be safe.
|
||||||
|
fn dist2(&self, other: &Point) -> u128 {
|
||||||
|
let dx = (self.x - other.x) as i128;
|
||||||
|
let dy = (self.y - other.y) as i128;
|
||||||
|
let dz = (self.z - other.z) as i128;
|
||||||
|
let s = dx * dx + dy * dy + dz * dz;
|
||||||
|
if s < 0 {
|
||||||
|
// should never happen, but cast safely
|
||||||
|
(-s) as u128
|
||||||
|
} else {
|
||||||
|
s as u128
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Edge between two indices with squared distance.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Edge {
|
||||||
|
a: usize,
|
||||||
|
b: usize,
|
||||||
|
dist2: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple union-find / disjoint-set with union by size + path compression.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct UnionFind {
|
||||||
|
parent: Vec<isize>, // if negative, -size; else parent index
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnionFind {
|
||||||
|
fn new(n: usize) -> Self {
|
||||||
|
UnionFind {
|
||||||
|
parent: vec![-1; n],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find(&mut self, x: usize) -> usize {
|
||||||
|
// path compression
|
||||||
|
let mut root = x;
|
||||||
|
while self.parent[root] >= 0 {
|
||||||
|
root = self.parent[root] as usize;
|
||||||
|
}
|
||||||
|
// compress
|
||||||
|
let mut cur = x;
|
||||||
|
while cur != root {
|
||||||
|
let next = self.parent[cur] as usize;
|
||||||
|
self.parent[cur] = root as isize;
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
root
|
||||||
|
}
|
||||||
|
|
||||||
|
fn union(&mut self, a: usize, b: usize) -> bool {
|
||||||
|
let mut ra = self.find(a);
|
||||||
|
let mut rb = self.find(b);
|
||||||
|
if ra == rb {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let sa = (-self.parent[ra]) as usize;
|
||||||
|
let sb = (-self.parent[rb]) as usize;
|
||||||
|
// union by size: attach smaller to larger
|
||||||
|
if sa < sb {
|
||||||
|
std::mem::swap(&mut ra, &mut rb);
|
||||||
|
}
|
||||||
|
// now ra has >= size
|
||||||
|
self.parent[ra] = -((sa + sb) as isize);
|
||||||
|
self.parent[rb] = ra as isize;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sizes(&self) -> Vec<usize> {
|
||||||
|
let n = self.parent.len();
|
||||||
|
let mut out = Vec::new();
|
||||||
|
for i in 0..n {
|
||||||
|
if self.parent[i] < 0 {
|
||||||
|
out.push((-self.parent[i]) as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Test from puzzle input
|
/// Test from puzzle input
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -44,7 +251,7 @@ mod test {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
assert_eq!(answer, "Ready");
|
assert_eq!(answer, "20");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -61,6 +268,6 @@ mod test {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
assert_eq!(answer, "Ready");
|
assert_eq!(answer, "25272");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user