mirror of
https://github.com/RussellChamp/advent-of-code-2025-cursor.git
synced 2026-10-27 23:52:34 +00:00
Solve Day 6 & 8, refactor Day 6 with itertools
This commit is contained in:
1 parent
5e0660b095
commit
e3fe456616
6 files changed
+339
-1
No files matched your search
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
When generating code, consider utilizing libraries that have been added to the project (such as itertools) if they make the resulting code more performant or readable.
|
||||||
@@ -16,8 +16,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
|
|||||||
| [Day 3](./src/bin/03.rs) | `85.1µs` | `121.6µs` |
|
| [Day 3](./src/bin/03.rs) | `85.1µs` | `121.6µs` |
|
||||||
| [Day 4](./src/bin/04.rs) | `244.2µs` | `4.1ms` |
|
| [Day 4](./src/bin/04.rs) | `244.2µs` | `4.1ms` |
|
||||||
| [Day 5](./src/bin/05.rs) | `196.7µs` | `66.3µs` |
|
| [Day 5](./src/bin/05.rs) | `196.7µs` | `66.3µs` |
|
||||||
|
| [Day 6](./src/bin/06.rs) | `109.0µs` | `194.2µs` |
|
||||||
|
|
||||||
**Total: 5.49ms**
|
**Total: 5.79ms**
|
||||||
<!--- benchmarking table --->
|
<!--- benchmarking table --->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
123 328 51 64
|
||||||
|
45 64 387 23
|
||||||
|
6 98 215 314
|
||||||
|
* + * +
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
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
|
||||||
|
|
||||||
+155
@@ -0,0 +1,155 @@
|
|||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
advent_of_code::solution!(6);
|
||||||
|
|
||||||
|
/// Calculate result for a problem given numbers and operator
|
||||||
|
fn calculate_result(numbers: &[u64], operator: &str) -> u64 {
|
||||||
|
match operator {
|
||||||
|
"*" => numbers.iter().product(),
|
||||||
|
"+" => numbers.iter().sum(),
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse problems from the worksheet and calculate the grand total
|
||||||
|
fn solve_worksheet(input: &str) -> u64 {
|
||||||
|
let lines: Vec<&str> = input.lines().filter(|l| !l.is_empty()).collect();
|
||||||
|
if lines.is_empty() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse each line into tokens using whitespace splitting
|
||||||
|
let rows: Vec<Vec<&str>> = lines
|
||||||
|
.iter()
|
||||||
|
.map(|line| line.split_ascii_whitespace().collect())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Split into operator row and number rows
|
||||||
|
let (number_rows, operator_row) = rows.split_at(rows.len() - 1);
|
||||||
|
let operator_row = &operator_row[0];
|
||||||
|
|
||||||
|
// Process each problem column and sum results
|
||||||
|
(0..operator_row.len())
|
||||||
|
.map(|problem_idx| {
|
||||||
|
let operator = operator_row.get(problem_idx).copied().unwrap_or("+");
|
||||||
|
let numbers: Vec<u64> = number_rows
|
||||||
|
.iter()
|
||||||
|
.filter_map(|row| row.get(problem_idx))
|
||||||
|
.filter_map(|s| s.parse().ok())
|
||||||
|
.collect();
|
||||||
|
calculate_result(&numbers, operator)
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
|
Some(solve_worksheet(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate result for a problem given numbers and operator (char version)
|
||||||
|
fn calculate_result_char(numbers: &[u64], operator: char) -> u64 {
|
||||||
|
match operator {
|
||||||
|
'*' => numbers.iter().product(),
|
||||||
|
'+' => numbers.iter().sum(),
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find problem boundaries from column space information
|
||||||
|
/// Returns ranges of (start, end) for each problem
|
||||||
|
fn find_problem_boundaries(grid: &[Vec<char>], max_len: usize) -> Vec<(usize, usize)> {
|
||||||
|
(0..max_len)
|
||||||
|
.map(|col| grid.iter().all(|row| row[col] == ' '))
|
||||||
|
.enumerate()
|
||||||
|
.chunk_by(|&(_, is_space)| is_space)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(is_space, group)| {
|
||||||
|
if is_space {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let cols: Vec<_> = group.collect();
|
||||||
|
let start = cols.first()?.0;
|
||||||
|
let end = cols.last()?.0 + 1;
|
||||||
|
Some((start, end))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse problems using cephalopod math (right-to-left columns)
|
||||||
|
/// This requires parsing the actual character grid to preserve column positions
|
||||||
|
fn solve_worksheet_cephalopod(input: &str) -> u64 {
|
||||||
|
let lines: Vec<&str> = input.lines().filter(|l| !l.is_empty()).collect();
|
||||||
|
if lines.is_empty() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find max line length and build padded grid
|
||||||
|
let max_len = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
||||||
|
let grid: Vec<Vec<char>> = lines
|
||||||
|
.iter()
|
||||||
|
.map(|l| {
|
||||||
|
let mut chars: Vec<char> = l.chars().collect();
|
||||||
|
chars.resize(max_len, ' ');
|
||||||
|
chars
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if grid.len() < 2 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split into operator row and number rows
|
||||||
|
let (number_rows, operator_rows) = grid.split_at(grid.len() - 1);
|
||||||
|
let operator_row = &operator_rows[0];
|
||||||
|
|
||||||
|
// Find problem boundaries and process each
|
||||||
|
find_problem_boundaries(&grid, max_len)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(start, end)| {
|
||||||
|
// Find operator for this problem
|
||||||
|
let operator = (start..end)
|
||||||
|
.find_map(|col| {
|
||||||
|
let c = operator_row[col];
|
||||||
|
(c == '*' || c == '+').then_some(c)
|
||||||
|
})
|
||||||
|
.unwrap_or('+');
|
||||||
|
|
||||||
|
// Read columns right-to-left, each column forms a number
|
||||||
|
let numbers: Vec<u64> = (start..end)
|
||||||
|
.rev()
|
||||||
|
.filter_map(|col| {
|
||||||
|
let digits: String = number_rows
|
||||||
|
.iter()
|
||||||
|
.map(|row| row[col])
|
||||||
|
.filter(|c| c.is_ascii_digit())
|
||||||
|
.collect();
|
||||||
|
digits.parse().ok()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
calculate_result_char(&numbers, operator)
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
|
Some(solve_worksheet_cephalopod(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, Some(4277556));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, Some(3263827));
|
||||||
|
}
|
||||||
|
}
|
||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
advent_of_code::solution!(8);
|
||||||
|
|
||||||
|
/// Union-Find data structure for tracking circuits
|
||||||
|
struct UnionFind {
|
||||||
|
parent: Vec<usize>,
|
||||||
|
rank: Vec<usize>,
|
||||||
|
size: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnionFind {
|
||||||
|
fn new(n: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
parent: (0..n).collect(),
|
||||||
|
rank: vec![0; n],
|
||||||
|
size: vec![1; n],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find(&mut self, x: usize) -> usize {
|
||||||
|
if self.parent[x] != x {
|
||||||
|
self.parent[x] = self.find(self.parent[x]); // Path compression
|
||||||
|
}
|
||||||
|
self.parent[x]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn union(&mut self, x: usize, y: usize) -> bool {
|
||||||
|
let root_x = self.find(x);
|
||||||
|
let root_y = self.find(y);
|
||||||
|
|
||||||
|
if root_x == root_y {
|
||||||
|
return false; // Already in same circuit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union by rank
|
||||||
|
match self.rank[root_x].cmp(&self.rank[root_y]) {
|
||||||
|
std::cmp::Ordering::Less => {
|
||||||
|
self.parent[root_x] = root_y;
|
||||||
|
self.size[root_y] += self.size[root_x];
|
||||||
|
}
|
||||||
|
std::cmp::Ordering::Greater => {
|
||||||
|
self.parent[root_y] = root_x;
|
||||||
|
self.size[root_x] += self.size[root_y];
|
||||||
|
}
|
||||||
|
std::cmp::Ordering::Equal => {
|
||||||
|
self.parent[root_y] = root_x;
|
||||||
|
self.size[root_x] += self.size[root_y];
|
||||||
|
self.rank[root_x] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_circuit_sizes(&mut self) -> Vec<usize> {
|
||||||
|
let n = self.parent.len();
|
||||||
|
let mut sizes = Vec::new();
|
||||||
|
for i in 0..n {
|
||||||
|
if self.find(i) == i {
|
||||||
|
sizes.push(self.size[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sizes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a line "X,Y,Z" into coordinates
|
||||||
|
fn parse_coord(line: &str) -> Option<(i64, i64, i64)> {
|
||||||
|
let mut parts = line.split(',');
|
||||||
|
let x = parts.next()?.parse().ok()?;
|
||||||
|
let y = parts.next()?.parse().ok()?;
|
||||||
|
let z = parts.next()?.parse().ok()?;
|
||||||
|
Some((x, y, z))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate squared Euclidean distance (to avoid sqrt)
|
||||||
|
fn distance_squared(a: (i64, i64, i64), b: (i64, i64, i64)) -> i64 {
|
||||||
|
let dx = a.0 - b.0;
|
||||||
|
let dy = a.1 - b.1;
|
||||||
|
let dz = a.2 - b.2;
|
||||||
|
dx * dx + dy * dy + dz * dz
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve(input: &str, num_connections: usize) -> Option<u64> {
|
||||||
|
let coords: Vec<(i64, i64, i64)> = input
|
||||||
|
.lines()
|
||||||
|
.filter(|line| !line.trim().is_empty())
|
||||||
|
.filter_map(parse_coord)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let n = coords.len();
|
||||||
|
if n == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate all pairs with distances
|
||||||
|
let mut pairs: Vec<(i64, usize, usize)> = Vec::with_capacity(n * (n - 1) / 2);
|
||||||
|
for i in 0..n {
|
||||||
|
for j in (i + 1)..n {
|
||||||
|
let dist = distance_squared(coords[i], coords[j]);
|
||||||
|
pairs.push((dist, i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by distance
|
||||||
|
pairs.sort_unstable_by_key(|&(dist, _, _)| dist);
|
||||||
|
|
||||||
|
// Process connections using Union-Find
|
||||||
|
let mut uf = UnionFind::new(n);
|
||||||
|
let mut connections_made = 0;
|
||||||
|
|
||||||
|
for (_, i, j) in pairs {
|
||||||
|
if connections_made >= num_connections {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Try to connect - even if already connected, it counts as one of our connections
|
||||||
|
uf.union(i, j);
|
||||||
|
connections_made += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get circuit sizes and find 3 largest
|
||||||
|
let mut sizes = uf.get_circuit_sizes();
|
||||||
|
sizes.sort_unstable_by(|a, b| b.cmp(a)); // Sort descending
|
||||||
|
|
||||||
|
// Multiply 3 largest
|
||||||
|
let result: u64 = sizes.iter().take(3).map(|&s| s as u64).product();
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<u64> {
|
||||||
|
solve(input, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<u64> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
// Example uses 10 connections, not 1000
|
||||||
|
let result = solve(&advent_of_code::template::read_file("examples", DAY), 10);
|
||||||
|
assert_eq!(result, Some(40));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user