Day 12
This commit is contained in:
parent
78f7f2db49
commit
04f4adc410
|
@ -0,0 +1,129 @@
|
|||
use crate::util::maps::*;
|
||||
|
||||
const DISCOUNT: bool = true;
|
||||
|
||||
pub fn day12() {
|
||||
let mut plots: Map<char> = Map::from_string(&crate::input(12));
|
||||
|
||||
let mut regions: Vec<Region> = vec![];
|
||||
for (x, y) in plots.coordinates() {
|
||||
if plots.get(x, y) != Some('.') {
|
||||
let region = Region::new(x, y, &plots);
|
||||
for (x, y) in plots.coordinates() {
|
||||
if region.tiles.get(x, y).unwrap() {
|
||||
plots.set(x, y, '.');
|
||||
}
|
||||
}
|
||||
regions.push(region);
|
||||
}
|
||||
}
|
||||
|
||||
let mut total_cost: u64 = 0;
|
||||
for region in regions {
|
||||
let mut sides: u64 = 0;
|
||||
let mut borders = region.borders.clone();
|
||||
|
||||
while let Some(border) = borders.pop() {
|
||||
let mut combined = vec![border];
|
||||
|
||||
while let Some(pos) = borders
|
||||
.iter()
|
||||
.position(|other| combined.iter().any(|b| b.contiguous_straight(other)))
|
||||
{
|
||||
combined.push(borders.remove(pos));
|
||||
}
|
||||
|
||||
sides += 1;
|
||||
}
|
||||
|
||||
let area = region.tiles.iter().filter(|&&t| t).count() as u64;
|
||||
|
||||
total_cost += if DISCOUNT {
|
||||
area * sides
|
||||
} else {
|
||||
area * region.borders.len() as u64
|
||||
};
|
||||
}
|
||||
|
||||
println!("Total Cost: {}", total_cost);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
struct Border {
|
||||
x: u64,
|
||||
y: u64,
|
||||
facing: Direction,
|
||||
}
|
||||
|
||||
impl Border {
|
||||
fn new(x: u64, y: u64, facing: Direction) -> Self {
|
||||
Self { x, y, facing }
|
||||
}
|
||||
|
||||
fn contiguous_straight(&self, other: &Self) -> bool {
|
||||
if self.facing != other.facing {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.facing == Direction::North || self.facing == Direction::South {
|
||||
return self.y == other.y && self.x.max(other.x) - self.x.min(other.x) == 1;
|
||||
}
|
||||
if self.facing == Direction::West || self.facing == Direction::East {
|
||||
return self.x == other.x && self.y.max(other.y) - self.y.min(other.y) == 1;
|
||||
}
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
struct Region {
|
||||
pub borders: Vec<Border>,
|
||||
pub tiles: Map<bool>,
|
||||
}
|
||||
|
||||
impl Region {
|
||||
fn new(x: usize, y: usize, plots: &Map<char>) -> Self {
|
||||
let c = plots.get(x, y).unwrap();
|
||||
let mut tiles = Map::<bool>::from_dimensions(plots.width(), plots.height(), false);
|
||||
|
||||
fn collect_plots(
|
||||
c: char,
|
||||
x: usize,
|
||||
y: usize,
|
||||
plots: &Map<char>,
|
||||
tiles: &mut Map<bool>,
|
||||
) -> Vec<(usize, usize)> {
|
||||
if plots.get(x, y) != Some(c) || tiles.get(x, y) == Some(true) {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
tiles.set(x, y, true);
|
||||
|
||||
let mut region = vec![(x, y)];
|
||||
for (nx, ny) in Direction::all_variants()
|
||||
.into_iter()
|
||||
.filter_map(|d| plots.travel(x, y, d))
|
||||
{
|
||||
region.extend(collect_plots(c, nx, ny, plots, tiles));
|
||||
}
|
||||
|
||||
region
|
||||
}
|
||||
|
||||
let collected_plots = collect_plots(c, x, y, plots, &mut tiles);
|
||||
|
||||
let borders = collected_plots
|
||||
.iter()
|
||||
.flat_map(|&(x, y)| {
|
||||
Direction::all_variants().into_iter().filter_map(move |d| {
|
||||
match plots.travel_get(x, y, d) {
|
||||
Some(o) if o == c => None,
|
||||
_ => Some(Border::new(x as u64, y as u64, d)),
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self { borders, tiles }
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
pub mod util;
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod day1;
|
||||
#[allow(dead_code)]
|
||||
|
@ -18,10 +20,12 @@ mod day8;
|
|||
mod day9;
|
||||
#[allow(dead_code)]
|
||||
mod day10;
|
||||
#[allow(dead_code)]
|
||||
mod day11;
|
||||
mod day12;
|
||||
|
||||
fn main() {
|
||||
day11::day11();
|
||||
day12::day12();
|
||||
}
|
||||
|
||||
pub fn input(day: u8) -> String {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
pub mod maps;
|
|
@ -0,0 +1,108 @@
|
|||
use itertools::Itertools;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
North,
|
||||
South,
|
||||
East,
|
||||
West,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
pub fn all_variants() -> Vec<Self> {
|
||||
vec![Self::North, Self::East, Self::West, Self::South]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Map<T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
pub map: Vec<Vec<T>>,
|
||||
}
|
||||
|
||||
impl Map<char> {
|
||||
pub fn from_string(string: &str) -> Self {
|
||||
Map::from_2d_vec(
|
||||
string
|
||||
.lines()
|
||||
.filter(|line| !line.is_empty())
|
||||
.map(|line| line.chars().collect())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Map<T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
pub fn from_2d_vec(map: Vec<Vec<T>>) -> Self {
|
||||
Self { map }
|
||||
}
|
||||
|
||||
pub fn from_dimensions(width: usize, height: usize, default: T) -> Self {
|
||||
Self {
|
||||
map: vec![vec![default; width]; height],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn travel(&self, x: usize, y: usize, direction: Direction) -> Option<(usize, usize)> {
|
||||
match direction {
|
||||
Direction::North => Some((x, y.checked_sub(1)?)),
|
||||
Direction::South => {
|
||||
if y + 1 < self.height() {
|
||||
Some((x, y + 1))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Direction::West => Some((x.checked_sub(1)?, y)),
|
||||
Direction::East => {
|
||||
if x + 1 < self.width() {
|
||||
Some((x + 1, y))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn travel_get(&self, x: usize, y: usize, direction: Direction) -> Option<T> {
|
||||
let (nx, ny) = self.travel(x, y, direction)?;
|
||||
self.get(nx, ny)
|
||||
}
|
||||
|
||||
pub fn get(&self, x: usize, y: usize) -> Option<T> {
|
||||
Some(*self.map.get(y)?.get(x)?)
|
||||
}
|
||||
|
||||
pub fn set(&mut self, x: usize, y: usize, value: T) {
|
||||
self.map[y][x] = value
|
||||
}
|
||||
|
||||
pub fn height(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.map[0].len()
|
||||
}
|
||||
|
||||
pub fn in_bounds(&self, x: usize, y: usize) -> bool {
|
||||
x < self.width() && y < self.height()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||
self.map.iter().flat_map(|row| row.iter())
|
||||
}
|
||||
|
||||
pub fn coordinates(&self) -> impl Iterator<Item = (usize, usize)> {
|
||||
(0..self.width()).cartesian_product(0..self.height())
|
||||
}
|
||||
|
||||
pub fn enumerate(&self) -> impl Iterator<Item = (usize, usize, &T)> {
|
||||
self.coordinates().map(|(x, y)| (x, y, &self.map[y][x]))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue