day12: complete solution
This commit is contained in:
parent
31eb500832
commit
a56fc933c9
169
src/day12.rs
Normal file
169
src/day12.rs
Normal file
@ -0,0 +1,169 @@
|
||||
use std::io::BufRead;
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use grid::{Coord2d, Grid};
|
||||
|
||||
pub struct Farm {
|
||||
map: Grid<u8>,
|
||||
}
|
||||
|
||||
impl From<&[u8]> for Farm {
|
||||
fn from(input: &[u8]) -> Self {
|
||||
Self {
|
||||
map: Grid::from(input.lines()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Farm {
|
||||
fn compute_region(&self, pos: &Coord2d, visited: &mut Grid<bool>) -> (u64, u64) {
|
||||
let our_plant = self.map.get(pos).unwrap();
|
||||
let mut perimeter = 0;
|
||||
let mut area = 1;
|
||||
|
||||
visited.set(pos, true);
|
||||
|
||||
for adj in [(-1i64, 0i64), (1, 0), (0, -1), (0, 1)].map(|ofs| pos + ofs) {
|
||||
match self.map.get(&adj) {
|
||||
Some(plant) if plant == our_plant => {
|
||||
if visited.get(&adj) == Some(false) {
|
||||
// add the perimeter of the growth from there if not visited yet
|
||||
let (n_area, n_perimeter) = self.compute_region(&adj, visited);
|
||||
area += n_area;
|
||||
perimeter += n_perimeter;
|
||||
}
|
||||
}
|
||||
Some(_) | None => perimeter += 1,
|
||||
}
|
||||
}
|
||||
(area, perimeter)
|
||||
}
|
||||
fn regions_cost(&self) -> u64 {
|
||||
let mut visited = Grid::with_shape(self.map.width(), self.map.height(), false);
|
||||
let mut cost = 0;
|
||||
for y in 0..self.map.height() {
|
||||
for x in 0..self.map.width() {
|
||||
cost += match visited.get(&(x, y)) {
|
||||
Some(false) => {
|
||||
let (area, perim) = self.compute_region(
|
||||
&Coord2d {
|
||||
x: x as i64,
|
||||
y: y as i64,
|
||||
},
|
||||
&mut visited,
|
||||
);
|
||||
area * perim
|
||||
}
|
||||
Some(_) | None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
cost
|
||||
}
|
||||
fn count_corners(&self, pos: &Coord2d) -> u64 {
|
||||
// A
|
||||
// AAA has 4 inside corners (pos at centre). check that AA A's exist and B doesn't for each rotation
|
||||
// A AB
|
||||
let our_plant = self.map.get(pos);
|
||||
let inside_corners = [(1i64, 1i64), (-1, 1), (1, -1), (-1, -1)]
|
||||
.iter()
|
||||
.filter(|inside_corner| {
|
||||
self.map.get(&(pos + **inside_corner)) != our_plant
|
||||
&& self.map.get(&(pos + (inside_corner.0, 0))) == our_plant
|
||||
&& self.map.get(&(pos + (0, inside_corner.1))) == our_plant
|
||||
})
|
||||
.count();
|
||||
let outside_corners = [(1i64, 1i64), (-1, 1), (1, -1), (-1, -1)]
|
||||
.iter()
|
||||
.filter(|outside_corner| {
|
||||
self.map.get(&(pos + (outside_corner.0, 0))) != our_plant
|
||||
&& self.map.get(&(pos + (0, outside_corner.1))) != our_plant
|
||||
})
|
||||
.count();
|
||||
(inside_corners + outside_corners) as u64
|
||||
}
|
||||
fn region_corners(&self, pos: &Coord2d, visited: &mut Grid<bool>) -> (u64, u64) {
|
||||
let our_plant = self.map.get(pos).unwrap();
|
||||
let mut area = 1;
|
||||
let mut corners = self.count_corners(pos);
|
||||
|
||||
visited.set(pos, true);
|
||||
|
||||
for adj in [(-1i64, 0i64), (1, 0), (0, -1), (0, 1)].map(|ofs| pos + ofs) {
|
||||
match self.map.get(&adj) {
|
||||
Some(plant) if plant == our_plant => {
|
||||
if visited.get(&adj) == Some(false) {
|
||||
// add the perimeter of the growth from there if not visited yet
|
||||
let (n_area, n_corners) = self.region_corners(&adj, visited);
|
||||
area += n_area;
|
||||
corners += n_corners;
|
||||
}
|
||||
}
|
||||
Some(_) | None => {}
|
||||
}
|
||||
}
|
||||
(area, corners)
|
||||
}
|
||||
fn regions_discount_cost(&self) -> u64 {
|
||||
let mut visited = Grid::with_shape(self.map.width(), self.map.height(), false);
|
||||
let mut cost = 0;
|
||||
for y in 0..self.map.height() {
|
||||
for x in 0..self.map.width() {
|
||||
cost += match visited.get(&(x, y)) {
|
||||
Some(false) => {
|
||||
let (area, corners) = self.region_corners(
|
||||
&Coord2d {
|
||||
x: x as i64,
|
||||
y: y as i64,
|
||||
},
|
||||
&mut visited,
|
||||
);
|
||||
area * corners
|
||||
}
|
||||
Some(_) | None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
cost
|
||||
}
|
||||
}
|
||||
|
||||
#[aoc_generator(day12)]
|
||||
fn parse(input: &[u8]) -> Farm {
|
||||
input.into()
|
||||
}
|
||||
|
||||
#[aoc(day12, part1)]
|
||||
pub fn part1(farm: &Farm) -> u64 {
|
||||
farm.regions_cost()
|
||||
}
|
||||
|
||||
#[aoc(day12, part2)]
|
||||
pub fn part2(farm: &Farm) -> u64 {
|
||||
farm.regions_discount_cost()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
const EXAMPLE: &[u8] = b"RRRRIICCFF
|
||||
RRRRIICCCF
|
||||
VVRRRCCFFF
|
||||
VVRCCCJFFF
|
||||
VVVVCJJCFE
|
||||
VVIVCCJJEE
|
||||
VVIIICJJEE
|
||||
MIIIIIJJEE
|
||||
MIIISIJEEE
|
||||
MMMISSJEEE";
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(&parse(EXAMPLE)), 1930);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(part2(&parse(EXAMPLE)), 1206);
|
||||
}
|
||||
}
|
@ -11,5 +11,6 @@ pub mod day8;
|
||||
pub mod day9;
|
||||
pub mod day10;
|
||||
pub mod day11;
|
||||
pub mod day12;
|
||||
|
||||
aoc_lib! { year = 2024 }
|
||||
|
@ -2,9 +2,10 @@ use std::{
|
||||
fmt::{Debug, Display, Formatter, Write},
|
||||
io::{BufRead, Lines},
|
||||
iter::repeat,
|
||||
ops::{Add, Sub},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Coord2d {
|
||||
pub x: i64,
|
||||
pub y: i64,
|
||||
@ -16,6 +17,26 @@ pub trait AsCoord2d {
|
||||
fn y(&self) -> i64;
|
||||
}
|
||||
|
||||
impl<T: AsCoord2d> Sub<T> for &Coord2d {
|
||||
type Output = Coord2d;
|
||||
fn sub(self, rhs: T) -> Self::Output {
|
||||
Coord2d {
|
||||
x: self.x() - rhs.x(),
|
||||
y: self.y() - rhs.y(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsCoord2d> Add<T> for &Coord2d {
|
||||
type Output = Coord2d;
|
||||
fn add(self, rhs: T) -> Self::Output {
|
||||
Coord2d {
|
||||
x: self.x() + rhs.x(),
|
||||
y: self.y() + rhs.y(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsCoord2d for Coord2d {
|
||||
fn to_coord(self) -> Coord2d {
|
||||
self
|
||||
@ -40,6 +61,21 @@ impl AsCoord2d for (i64, i64) {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsCoord2d for (usize, usize) {
|
||||
fn to_coord(self) -> Coord2d {
|
||||
Coord2d {
|
||||
x: self.0 as i64,
|
||||
y: self.1 as i64,
|
||||
}
|
||||
}
|
||||
fn x(&self) -> i64 {
|
||||
self.0 as i64
|
||||
}
|
||||
fn y(&self) -> i64 {
|
||||
self.1 as i64
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Grid<T> {
|
||||
pub data: Vec<T>,
|
||||
|
Loading…
x
Reference in New Issue
Block a user