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)]
|
#[allow(dead_code)]
|
||||||
mod day1;
|
mod day1;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -18,10 +20,12 @@ mod day8;
|
||||||
mod day9;
|
mod day9;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod day10;
|
mod day10;
|
||||||
|
#[allow(dead_code)]
|
||||||
mod day11;
|
mod day11;
|
||||||
|
mod day12;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
day11::day11();
|
day12::day12();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input(day: u8) -> String {
|
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