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(day06::Day06 {}),
|
||||
Box::new(day07::Day07 {}),
|
||||
// Box::new(day08::Day08 {}),
|
||||
Box::new(day08::Day08 {}),
|
||||
// Box::new(day09::Day09 {}),
|
||||
// Box::new(day10::Day10 {}),
|
||||
// Box::new(day11::Day11 {}),
|
||||
|
||||
@@ -45,10 +45,10 @@ impl Solution for Day06 {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(op) = op {
|
||||
if !numbers.is_empty() {
|
||||
final_problems.push((numbers, op));
|
||||
}
|
||||
if let Some(op) = op
|
||||
&& !numbers.is_empty()
|
||||
{
|
||||
final_problems.push((numbers, op));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,11 +78,8 @@ impl Solution for Day06 {
|
||||
|
||||
let mut problems: Vec<Vec<char>> = Vec::new();
|
||||
for col in 0..width {
|
||||
let mut column_chars = Vec::new();
|
||||
for row in 0..height {
|
||||
column_chars.push(grid[row][col]);
|
||||
}
|
||||
problems.push(column_chars);
|
||||
let column: Vec<char> = grid.iter().map(|row| row[col]).collect();
|
||||
problems.push(column);
|
||||
}
|
||||
|
||||
// Split columns into groups separated by all-space columns
|
||||
@@ -116,7 +113,7 @@ impl Solution for Day06 {
|
||||
let mut num_str = String::new();
|
||||
for &c in &col[..height - 1] {
|
||||
// all but the last row
|
||||
if c.is_digit(10) {
|
||||
if c.is_ascii_digit() {
|
||||
num_str.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ impl Solution for 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 width = lines[0].len();
|
||||
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;
|
||||
|
||||
pub struct Day08 {}
|
||||
@@ -5,16 +8,30 @@ pub struct Day08 {}
|
||||
impl Solution for Day08 {
|
||||
fn part1(
|
||||
&self,
|
||||
_input: &mut Vec<String>,
|
||||
input: &mut Vec<String>,
|
||||
) -> 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(
|
||||
&self,
|
||||
_input: &mut Vec<String>,
|
||||
input: &mut Vec<String>,
|
||||
) -> 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 {
|
||||
@@ -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
|
||||
#[cfg(test)]
|
||||
@@ -44,7 +251,7 @@ mod test {
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
assert_eq!(answer, "Ready");
|
||||
assert_eq!(answer, "20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -61,6 +268,6 @@ mod test {
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
assert_eq!(answer, "Ready");
|
||||
assert_eq!(answer, "25272");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user