From 8f7092f19eb6708676b95c93cc9012d57d80a06b Mon Sep 17 00:00:00 2001 From: Jonni Liljamo Date: Thu, 12 Dec 2024 18:53:59 +0200 Subject: [PATCH] feat: day12 --- input/day12/example | 10 ++ input/day12/example1 | 5 + input/day12/example2 | 6 + input/day12/examplesmall | 4 + src/day12/mod.rs | 11 ++ src/day12/part1.rs | 105 ++++++++++++++++ src/day12/part2.rs | 265 +++++++++++++++++++++++++++++++++++++++ src/main.rs | 8 ++ 8 files changed, 414 insertions(+) create mode 100644 input/day12/example create mode 100644 input/day12/example1 create mode 100644 input/day12/example2 create mode 100644 input/day12/examplesmall create mode 100644 src/day12/mod.rs create mode 100644 src/day12/part1.rs create mode 100644 src/day12/part2.rs diff --git a/input/day12/example b/input/day12/example new file mode 100644 index 0000000..85b768f --- /dev/null +++ b/input/day12/example @@ -0,0 +1,10 @@ +RRRRIICCFF +RRRRIICCCF +VVRRRCCFFF +VVRCCCJFFF +VVVVCJJCFE +VVIVCCJJEE +VVIIICJJEE +MIIIIIJJEE +MIIISIJEEE +MMMISSJEEE diff --git a/input/day12/example1 b/input/day12/example1 new file mode 100644 index 0000000..5818d30 --- /dev/null +++ b/input/day12/example1 @@ -0,0 +1,5 @@ +EEEEE +EXXXX +EEEEE +EXXXX +EEEEE diff --git a/input/day12/example2 b/input/day12/example2 new file mode 100644 index 0000000..f7cd38f --- /dev/null +++ b/input/day12/example2 @@ -0,0 +1,6 @@ +AAAAAA +AAABBA +AAABBA +ABBAAA +ABBAAA +AAAAAA diff --git a/input/day12/examplesmall b/input/day12/examplesmall new file mode 100644 index 0000000..b41163a --- /dev/null +++ b/input/day12/examplesmall @@ -0,0 +1,4 @@ +AAAA +BBCD +BBCC +EEEC diff --git a/src/day12/mod.rs b/src/day12/mod.rs new file mode 100644 index 0000000..3966e37 --- /dev/null +++ b/src/day12/mod.rs @@ -0,0 +1,11 @@ +use std::path::Path; + +mod part1; +mod part2; + +pub fn solve(input: &Path) -> anyhow::Result<()> { + println!("part one: {}", part1::part_one(input)?); + println!("part two: {}", part2::part_two(input)?); + + Ok(()) +} diff --git a/src/day12/part1.rs b/src/day12/part1.rs new file mode 100644 index 0000000..95a9f17 --- /dev/null +++ b/src/day12/part1.rs @@ -0,0 +1,105 @@ +use std::{ + collections::{BTreeSet, HashSet}, + fs::File, + io::{BufRead, BufReader}, + path::Path, +}; + +fn get_around(map: &[Vec], p: (usize, usize)) -> Vec<(char, usize, usize)> { + let mut around_us = vec![]; + if p.0 > 0 { + let (y, x) = (p.0 - 1, p.1); + around_us.push((map[y][x], y, x)); + } + if let Some(row) = map.get(p.0 + 1) { + let (y, x) = (p.0 + 1, p.1); + around_us.push((row[x], y, x)); + } + if p.1 > 0 { + let (y, x) = (p.0, p.1 - 1); + around_us.push((map[y][x], y, x)); + } + if let Some(f) = map[p.0].get(p.1 + 1) { + let (y, x) = (p.0, p.1 + 1); + around_us.push((*f, y, x)); + } + around_us +} + +#[derive(Debug, Eq, Hash, PartialEq)] +struct Region { + plots: BTreeSet, +} + +impl Region { + fn read_from_map(map: &[Vec], c: &char, p: (usize, usize)) -> Self { + let mut region = Region { + plots: BTreeSet::new(), + }; + region.plots.insert(Plot { + y: p.0, + x: p.1, + perimiter: (get_around(map, (p.0, p.1)) + .iter() + .filter(|(f, _, _)| f == c) + .collect::>() + .len() as i32 + - 4) + .unsigned_abs() as usize, + }); + region.read_recurse(map, c, p); + region + } + + fn read_recurse(&mut self, map: &[Vec], c: &char, p: (usize, usize)) { + for (f, y, x) in get_around(map, p) { + if f == *c { + let perimiter = (get_around(map, (y, x)) + .iter() + .filter(|(f, _, _)| f == c) + .collect::>() + .len() as i32 + - 4) + .unsigned_abs() as usize; + if self.plots.insert(Plot { y, x, perimiter }) { + self.read_recurse(map, c, (y, x)); + } + } + } + } + + fn get_fence_cost(&self) -> usize { + self.plots + .iter() + .map(|p| -> usize { p.perimiter }) + .sum::() + * self.plots.len() + } +} + +#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +struct Plot { + y: usize, + x: usize, + perimiter: usize, +} + +pub fn part_one(input: &Path) -> anyhow::Result { + let reader = BufReader::new(File::open(input)?); + + let mut map: Vec> = vec![]; + for line in reader.lines() { + map.push(line?.chars().collect()); + } + let mut regions: HashSet = HashSet::new(); + for (y, row) in map.iter().enumerate() { + for (x, c) in row.iter().enumerate() { + regions.insert(Region::read_from_map(&map, c, (y, x))); + } + } + + Ok(regions + .iter() + .map(|r| -> usize { r.get_fence_cost() }) + .sum()) +} diff --git a/src/day12/part2.rs b/src/day12/part2.rs new file mode 100644 index 0000000..03b3f4c --- /dev/null +++ b/src/day12/part2.rs @@ -0,0 +1,265 @@ +use std::{ + collections::{BTreeSet, HashSet}, + fs::File, + io::{BufRead, BufReader}, + path::Path, +}; + +fn get_around(map: &[Vec], p: (usize, usize)) -> Vec<(char, usize, usize)> { + let mut around_us = vec![]; + if p.0 > 0 { + let (y, x) = (p.0 - 1, p.1); + around_us.push((map[y][x], y, x)); + } + if let Some(row) = map.get(p.0 + 1) { + let (y, x) = (p.0 + 1, p.1); + around_us.push((row[x], y, x)); + } + if p.1 > 0 { + let (y, x) = (p.0, p.1 - 1); + around_us.push((map[y][x], y, x)); + } + if let Some(f) = map[p.0].get(p.1 + 1) { + let (y, x) = (p.0, p.1 + 1); + around_us.push((*f, y, x)); + } + around_us +} + +#[derive(Debug, Eq, Hash, PartialEq)] +struct Region { + c: char, + plots: BTreeSet, +} + +impl Region { + fn read_from_map(map: &[Vec], c: &char, p: (usize, usize)) -> Self { + let mut region = Region { + c: *c, + plots: BTreeSet::new(), + }; + region.plots.insert(Plot { + y: p.0, + x: p.1, + perimiter: (get_around(map, (p.0, p.1)) + .iter() + .filter(|(f, _, _)| f == c) + .collect::>() + .len() as i32 + - 4) + .unsigned_abs() as usize, + }); + region.read_recurse(map, c, p); + region + } + + fn read_recurse(&mut self, map: &[Vec], c: &char, p: (usize, usize)) { + for (f, y, x) in get_around(map, p) { + if f == *c { + let perimiter = (get_around(map, (y, x)) + .iter() + .filter(|(f, _, _)| f == c) + .collect::>() + .len() as i32 + - 4) + .unsigned_abs() as usize; + if self.plots.insert(Plot { y, x, perimiter }) { + self.read_recurse(map, c, (y, x)); + } + } + } + } + + fn get_fence_cost(&self, map: &[Vec], max_pos: usize) -> usize { + // Okay so, instead of perimiters * len, sides * len + // form all possible rows and columns of plots + // AAAA + // BB, B, B, BB + // B, B + // C, C, CC, C + // C, C + // D + // EEE + + let mut rows_initial: Vec> = vec![]; + for i in 0..max_pos { + let mut a: Vec = self.plots.iter().filter(|p| p.y == i).cloned().collect(); + if !a.is_empty() { + a.sort_by_key(|p| p.x); + rows_initial.push(a); + } + } + let mut rows: Vec> = vec![]; + for row in &rows_initial { + let mut real_rows: Vec> = vec![]; + let mut last = None; + let mut current_real_row: Vec = vec![]; + for p in row { + if last.is_none() { + current_real_row.push(p.clone()); + last = Some(p); + continue; + } + + if p.x != last.unwrap().x + 1 { + real_rows.push(current_real_row.clone()); + current_real_row.clear(); + } + current_real_row.push(p.clone()); + last = Some(p); + } + real_rows.push(current_real_row.clone()); + rows.append(&mut real_rows); + } + + let mut row_sides = 0; + for row in rows { + // top + let mut sides = 0; + let mut side_length = 0; + for p in &row { + let fence = if p.y > 0 { + map[p.y - 1][p.x] != self.c + } else { + true + }; + if fence { + side_length += 1; + } else if side_length > 0 { + sides += 1; + side_length = 0; + } + } + if side_length > 0 { + sides += 1; + } + row_sides += sides; + + // bottom + let mut sides = 0; + let mut side_length = 0; + for p in &row { + let fence = if let Some(below) = map.get(p.y + 1) { + below[p.x] != self.c + } else { + true + }; + if fence { + side_length += 1; + } else if side_length > 0 { + sides += 1; + side_length = 0; + } + } + if side_length > 0 { + sides += 1; + } + row_sides += sides; + } + + let mut cols_initial: Vec> = vec![]; + for i in 0..max_pos { + let mut a: Vec = self.plots.iter().filter(|p| p.x == i).cloned().collect(); + if !a.is_empty() { + a.sort_by_key(|p| p.y); + cols_initial.push(a); + } + } + let mut cols: Vec> = vec![]; + for col in &cols_initial { + let mut real_cols: Vec> = vec![]; + let mut last = None; + let mut current_real_col: Vec = vec![]; + for p in col { + if last.is_none() { + current_real_col.push(p.clone()); + last = Some(p); + continue; + } + + if p.y != last.unwrap().y + 1 { + real_cols.push(current_real_col.clone()); + current_real_col.clear(); + } + current_real_col.push(p.clone()); + last = Some(p); + } + real_cols.push(current_real_col.clone()); + cols.append(&mut real_cols); + } + + let mut col_sides = 0; + for col in cols { + // right + let mut sides = 0; + let mut side_length = 0; + for p in &col { + let fence = if p.x > 0 { + map[p.y][p.x - 1] != self.c + } else { + true + }; + if fence { + side_length += 1; + } else if side_length > 0 { + sides += 1; + side_length = 0; + } + } + if side_length > 0 { + sides += 1; + } + col_sides += sides; + + // left + let mut sides = 0; + let mut side_length = 0; + for p in &col { + let fence = if let Some(left) = map[p.y].get(p.x + 1) { + *left != self.c + } else { + true + }; + if fence { + side_length += 1; + } else if side_length > 0 { + sides += 1; + side_length = 0; + } + } + if side_length > 0 { + sides += 1; + } + col_sides += sides; + } + + self.plots.len() * (row_sides + col_sides) + } +} + +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +struct Plot { + y: usize, + x: usize, + perimiter: usize, +} + +pub fn part_two(input: &Path) -> anyhow::Result { + let reader = BufReader::new(File::open(input)?); + + let mut map: Vec> = vec![]; + for line in reader.lines() { + map.push(line?.chars().collect()); + } + let mut regions: HashSet = HashSet::new(); + for (y, row) in map.iter().enumerate() { + for (x, c) in row.iter().enumerate() { + regions.insert(Region::read_from_map(&map, c, (y, x))); + } + } + + Ok(regions + .iter() + .map(|r| -> usize { r.get_fence_cost(&map, map.len()) }) + .sum()) +} diff --git a/src/main.rs b/src/main.rs index b944cf4..a0fdb09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use clap::{Parser, Subcommand}; mod day1; mod day10; mod day11; +mod day12; mod day2; mod day3; mod day4; @@ -66,6 +67,10 @@ enum DayArgs { #[arg(short)] input: PathBuf, }, + Day12 { + #[arg(short)] + input: PathBuf, + }, } fn main() -> anyhow::Result<()> { @@ -105,6 +110,9 @@ fn main() -> anyhow::Result<()> { DayArgs::Day11 { input } => { day11::solve(&input)?; } + DayArgs::Day12 { input } => { + day12::solve(&input)?; + } } Ok(()) -- 2.44.1