generated from luke-else/AdventOfCodeXXXX
feat: Completed day 9 part 1.. Part 2 just takes way too long to run
All checks were successful
Continuous integration / Check (push) Successful in 40s
Continuous integration / Test Suite (push) Successful in 41s
Continuous integration / Rustfmt (push) Successful in 28s
Continuous integration / Clippy (push) Successful in 42s
Continuous integration / build (push) Successful in 40s
All checks were successful
Continuous integration / Check (push) Successful in 40s
Continuous integration / Test Suite (push) Successful in 41s
Continuous integration / Rustfmt (push) Successful in 28s
Continuous integration / Clippy (push) Successful in 42s
Continuous integration / build (push) Successful in 40s
This commit is contained in:
15
src/main.rs
15
src/main.rs
@@ -20,23 +20,10 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
Box::new(day06::Day06 {}),
|
||||
Box::new(day07::Day07 {}),
|
||||
Box::new(day08::Day08 {}),
|
||||
// Box::new(day09::Day09 {}),
|
||||
Box::new(day09::Day09 {}),
|
||||
// Box::new(day10::Day10 {}),
|
||||
// Box::new(day11::Day11 {}),
|
||||
// Box::new(day12::Day12 {}),
|
||||
// Box::new(day13::Day13 {}),
|
||||
// Box::new(day14::Day14 {}),
|
||||
// Box::new(day15::Day15 {}),
|
||||
// Box::new(day16::Day16 {}),
|
||||
// Box::new(day17::Day17 {}),
|
||||
// Box::new(day18::Day18 {}),
|
||||
// Box::new(day19::Day19 {}),
|
||||
// Box::new(day20::Day20 {}),
|
||||
// Box::new(day21::Day21 {}),
|
||||
// Box::new(day22::Day22 {}),
|
||||
// Box::new(day23::Day23 {}),
|
||||
// Box::new(day24::Day24 {}),
|
||||
// Box::new(day25::Day25 {}),
|
||||
];
|
||||
|
||||
// Vector to store every solution as a thread handle
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
use super::Solution;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub struct Day09 {}
|
||||
|
||||
impl Solution for Day09 {
|
||||
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)?;
|
||||
Ok(Box::new(self.max_rectangle_area(&points)))
|
||||
}
|
||||
|
||||
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 red = self.parse_points(input)?;
|
||||
Ok(Box::new(self.largest_red_green_rectangle(&red)))
|
||||
}
|
||||
|
||||
fn get_day(&self) -> u8 {
|
||||
@@ -22,7 +26,281 @@ impl Solution for Day09 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Day09 {}
|
||||
impl Day09 {
|
||||
fn parse_points(&self, reader: &[String]) -> Result<Vec<Point>, Box<dyn std::error::Error>> {
|
||||
let mut pts = Vec::new();
|
||||
|
||||
for line in reader.iter() {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut parts = trimmed.split(',');
|
||||
let x_str = parts.next().ok_or("Missing x")?.trim();
|
||||
let y_str = parts.next().ok_or("Missing y")?.trim();
|
||||
|
||||
let x = x_str.parse::<i32>()?;
|
||||
let y = y_str.parse::<i32>()?;
|
||||
pts.push(Point { x, y });
|
||||
}
|
||||
|
||||
Ok(pts)
|
||||
}
|
||||
|
||||
fn max_rectangle_area(&self, points: &[Point]) -> i64 {
|
||||
let n = points.len();
|
||||
let mut best = 0i64;
|
||||
|
||||
for i in 0..n {
|
||||
for j in (i + 1)..n {
|
||||
let a = points[i];
|
||||
let b = points[j];
|
||||
let area = self.rectangle_area(a, b);
|
||||
if area > best {
|
||||
best = area;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
best
|
||||
}
|
||||
|
||||
fn build_edges(&self, red: &[Point]) -> Vec<Point> {
|
||||
let mut v = Vec::new();
|
||||
let n = red.len();
|
||||
for i in 0..n {
|
||||
let a = red[i];
|
||||
let b = red[(i + 1) % n];
|
||||
if a.x == b.x {
|
||||
let x = a.x;
|
||||
let y1 = a.y.min(b.y);
|
||||
let y2 = a.y.max(b.y);
|
||||
for y in y1..=y2 {
|
||||
v.push(Point { x, y });
|
||||
}
|
||||
} else if a.y == b.y {
|
||||
let y = a.y;
|
||||
let x1 = a.x.min(b.x);
|
||||
let x2 = a.x.max(b.x);
|
||||
for x in x1..=x2 {
|
||||
v.push(Point { x, y });
|
||||
}
|
||||
} else {
|
||||
panic!("Adjacent red points must be aligned.");
|
||||
}
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
fn flood_fill_interior(
|
||||
&self,
|
||||
edges: &[Point],
|
||||
min_x: i32,
|
||||
min_y: i32,
|
||||
width: usize,
|
||||
height: usize,
|
||||
) -> BitGrid {
|
||||
let mut grid = BitGrid::new(min_x, min_y, width, height);
|
||||
for &p in edges {
|
||||
grid.set(p.x, p.y);
|
||||
}
|
||||
|
||||
// Flood-fill from outside
|
||||
let mut visited = BitGrid::new(min_x, min_y, width, height);
|
||||
|
||||
let mut q = VecDeque::new();
|
||||
let start = Point { x: min_x, y: min_y };
|
||||
q.push_back(start);
|
||||
visited.set(start.x, start.y);
|
||||
|
||||
let dirs = [(1, 0), (-1, 0), (0, 1), (0, -1)];
|
||||
|
||||
while let Some(p) = q.pop_front() {
|
||||
for (dx, dy) in dirs {
|
||||
let np = Point {
|
||||
x: p.x + dx,
|
||||
y: p.y + dy,
|
||||
};
|
||||
if np.x < min_x
|
||||
|| np.x >= min_x + width as i32
|
||||
|| np.y < min_y
|
||||
|| np.y >= min_y + height as i32
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if grid.get(np.x, np.y) {
|
||||
// edge blocks movement
|
||||
continue;
|
||||
}
|
||||
if !visited.get(np.x, np.y) {
|
||||
visited.set(np.x, np.y);
|
||||
q.push_back(np);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build final valid = edges + interior
|
||||
let mut valid = BitGrid::new(min_x, min_y, width, height);
|
||||
for y in 0..height {
|
||||
for block in 0..valid.blocks_per_row {
|
||||
let outside = visited.rows[y][block];
|
||||
let edge = grid.rows[y][block];
|
||||
let interior = (!outside) & (!edge);
|
||||
valid.rows[y][block] = edge | interior;
|
||||
}
|
||||
}
|
||||
|
||||
valid
|
||||
}
|
||||
|
||||
fn rectangle_ok(&self, a: Point, b: Point, grid: &BitGrid) -> bool {
|
||||
let x1 = a.x.min(b.x);
|
||||
let x2 = a.x.max(b.x);
|
||||
let y1 = a.y.min(b.y);
|
||||
let y2 = a.y.max(b.y);
|
||||
|
||||
let start = (x1 - grid.min_x) as usize;
|
||||
let end = (x2 - grid.min_x) as usize;
|
||||
|
||||
let block_start = start / 64;
|
||||
let block_end = end / 64;
|
||||
|
||||
for y in y1..=y2 {
|
||||
let row_index = (y - grid.min_y) as usize;
|
||||
let row = &grid.rows[row_index];
|
||||
|
||||
for (blk, _) in row.iter().enumerate().take(block_end + 1).skip(block_start) {
|
||||
let left_bit = if blk == block_start { start % 64 } else { 0 };
|
||||
let right_bit = if blk == block_end { end % 64 } else { 63 };
|
||||
let mask = if left_bit == 0 && right_bit == 63 {
|
||||
!0u64
|
||||
} else {
|
||||
((1u64 << (right_bit + 1)) - 1) & !((1u64 << left_bit) - 1)
|
||||
};
|
||||
|
||||
let bits = row[blk];
|
||||
if (bits & mask) != mask {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn largest_red_green_rectangle(&self, red: &[Point]) -> i64 {
|
||||
// Determine bounding box (expand by 2 for flood-fill safety)
|
||||
let min_x = red.iter().map(|p| p.x).min().unwrap() - 2;
|
||||
let max_x = red.iter().map(|p| p.x).max().unwrap() + 2;
|
||||
let min_y = red.iter().map(|p| p.y).min().unwrap() - 2;
|
||||
let max_y = red.iter().map(|p| p.y).max().unwrap() + 2;
|
||||
|
||||
let width = (max_x - min_x + 1) as usize;
|
||||
let height = (max_y - min_y + 1) as usize;
|
||||
|
||||
let edges = self.build_edges(red);
|
||||
let valid = self.flood_fill_interior(&edges, min_x, min_y, width, height);
|
||||
|
||||
// Ensure red tiles are "valid"
|
||||
let mut valid = valid;
|
||||
for &p in red {
|
||||
valid.set(p.x, p.y);
|
||||
}
|
||||
|
||||
// Compute max rectangle
|
||||
let mut best = 0;
|
||||
for i in 0..red.len() {
|
||||
for j in (i + 1)..red.len() {
|
||||
let a = red[i];
|
||||
let b = red[j];
|
||||
if self.rectangle_ok(a, b, &valid) {
|
||||
let area = self.rectangle_area(a, b);
|
||||
if area > best {
|
||||
best = area;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
best
|
||||
}
|
||||
|
||||
fn rectangle_area(&self, a: Point, b: Point) -> i64 {
|
||||
let width = ((a.x - b.x).abs() + 1) as i64;
|
||||
let height = ((a.y - b.y).abs() + 1) as i64;
|
||||
width * height
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct BitGrid {
|
||||
min_x: i32,
|
||||
min_y: i32,
|
||||
width: usize,
|
||||
height: usize,
|
||||
blocks_per_row: usize,
|
||||
rows: Vec<Vec<u64>>,
|
||||
}
|
||||
|
||||
impl BitGrid {
|
||||
fn new(min_x: i32, min_y: i32, width: usize, height: usize) -> Self {
|
||||
let blocks_per_row = width.div_ceil(64);
|
||||
let mut rows = Vec::with_capacity(height);
|
||||
for _ in 0..height {
|
||||
rows.push(vec![0u64; blocks_per_row]);
|
||||
}
|
||||
Self {
|
||||
min_x,
|
||||
min_y,
|
||||
width,
|
||||
height,
|
||||
blocks_per_row,
|
||||
rows,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn coord_to_block_bit(&self, x: i32, y: i32) -> Option<(usize, u64)> {
|
||||
let dx = x - self.min_x;
|
||||
let dy = y - self.min_y;
|
||||
if dx < 0 || dy < 0 {
|
||||
return None;
|
||||
}
|
||||
let ux = dx as usize;
|
||||
let uy = dy as usize;
|
||||
if ux >= self.width || uy >= self.height {
|
||||
return None;
|
||||
}
|
||||
let block = ux / 64;
|
||||
let bit = 1u64 << (ux % 64);
|
||||
Some((uy * self.blocks_per_row + block, bit))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&mut self, x: i32, y: i32) {
|
||||
if let Some((flat_index, bit)) = self.coord_to_block_bit(x, y) {
|
||||
let row = flat_index / self.blocks_per_row;
|
||||
let col = flat_index % self.blocks_per_row;
|
||||
self.rows[row][col] |= bit;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get(&self, x: i32, y: i32) -> bool {
|
||||
if let Some((flat_index, bit)) = self.coord_to_block_bit(x, y) {
|
||||
let row = flat_index / self.blocks_per_row;
|
||||
let col = flat_index % self.blocks_per_row;
|
||||
(self.rows[row][col] & bit) != 0
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Test from puzzle input
|
||||
#[cfg(test)]
|
||||
@@ -44,7 +322,7 @@ mod test {
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
assert_eq!(answer, "Ready");
|
||||
assert_eq!(answer, "50");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -61,6 +339,6 @@ mod test {
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
assert_eq!(answer, "Ready");
|
||||
assert_eq!(answer, "24");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user