This commit is contained in:
Hamcha 2024-12-13 10:58:35 +01:00
parent 401a086a71
commit f17388b5ec
7 changed files with 259 additions and 0 deletions

4
sample/day12-sample.txt Normal file
View file

@ -0,0 +1,4 @@
AAAA
BBCD
BBCC
EEEC

5
sample/day12-sample2.txt Normal file
View file

@ -0,0 +1,5 @@
OOOOO
OXOXO
OOOOO
OXOXO
OOOOO

10
sample/day12-sample3.txt Normal file
View file

@ -0,0 +1,10 @@
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE

5
sample/day12-sample4.txt Normal file
View file

@ -0,0 +1,5 @@
EEEEE
EXXXX
EEEEE
EXXXX
EEEEE

6
sample/day12-sample5.txt Normal file
View file

@ -0,0 +1,6 @@
AAAAAA
AAABBA
AAABBA
ABBAAA
ABBAAA
AAAAAA

97
src/day12_p1.gleam Normal file
View file

@ -0,0 +1,97 @@
import gleam/bool
import gleam/dict.{type Dict}
import gleam/int
import gleam/io
import gleam/iterator
import gleam/list.{contains, count, filter, fold, map, reverse}
import gleam/pair
import gleam/string
import stdin.{stdin}
pub fn main() {
stdin()
|> read_input
|> detect_regions
|> map(calculate_price)
|> fold(0, int.add)
|> io.debug
}
fn calculate_price(areas: List(#(Int, Int))) {
let area = areas |> list.length
let perimeter =
areas
|> fold(0, fn(acc, area) {
let #(x, y) = area
let bounds =
[#(x + 1, y), #(x - 1, y), #(x, y + 1), #(x, y - 1)]
|> count(fn(point) { areas |> contains(point) |> bool.negate })
acc + bounds
})
area * perimeter
}
fn detect_regions(textmap: Dict(#(Int, Int), String)) -> List(List(#(Int, Int))) {
textmap
|> dict.keys
|> fold(#(textmap, []), fn(acc, x) {
let #(searchspace, lists) = acc
// Make sure we haven't already included this key in another region
case lists |> list.find(fn(lst) { lst |> contains(x) }) {
Ok(_) -> acc
_ -> {
let assert Ok(plant) = dict.get(textmap, x)
find_region(searchspace, x, plant, [])
|> pair.map_second(fn(lst) { [lst |> list.unique, ..lists] })
}
}
})
|> pair.second
}
fn find_region(
textmap: Dict(#(Int, Int), String),
start: #(Int, Int),
plant: String,
processed: List(#(Int, Int)),
) -> #(Dict(#(Int, Int), String), List(#(Int, Int))) {
let #(x, y) = start
let searchspace = dict.delete(textmap, start)
[#(x - 1, y), #(x + 1, y), #(x, y - 1), #(x, y + 1)]
|> filter(fn(x) { is_same(textmap, x, plant) })
|> fold(#(searchspace, [start, ..processed]), fn(acc, x) {
let #(partialmap, current) = acc
find_region(partialmap, x, plant, current)
})
}
fn is_same(
textmap: Dict(#(Int, Int), String),
point: #(Int, Int),
plant: String,
) -> Bool {
case dict.get(textmap, point) {
Ok(s) -> s == plant
_ -> False
}
}
fn read_input(it: iterator.Iterator(String)) -> Dict(#(Int, Int), String) {
it
|> iterator.fold([], fn(acc, line) {
case string.trim(line) {
"" -> acc
str -> {
[str |> string.to_graphemes, ..acc]
}
}
})
|> reverse
|> list.index_fold(dict.new(), fn(acc, line, y) {
line
|> list.index_fold(acc, fn(dic, num, x) { dict.insert(dic, #(x, y), num) })
})
}

132
src/day12_p2.gleam Normal file
View file

@ -0,0 +1,132 @@
import gleam/bool
import gleam/dict.{type Dict}
import gleam/int
import gleam/io
import gleam/iterator
import gleam/list.{contains, filter, fold, map, reverse}
import gleam/pair
import gleam/string
import stdin.{stdin}
pub fn main() {
stdin()
|> read_input
|> detect_regions
|> map(calculate_price)
|> fold(0, int.add)
|> io.debug
}
fn calculate_price(areas: List(#(Int, Int))) {
let area = areas |> list.length
let perimeter =
areas
|> fold([], fn(acc, area) {
let bounds = find_bounds(areas, area)
list.append(acc, bounds)
})
|> count_contiguous
area * perimeter
}
type Bound {
Left
Right
Top
Bottom
}
fn find_bounds(
areas: List(#(Int, Int)),
area: #(Int, Int),
) -> List(#(Bound, #(Int, Int))) {
let #(x, y) = area
[
#(Right, #(x + 1, y)),
#(Left, #(x - 1, y)),
#(Bottom, #(x, y + 1)),
#(Top, #(x, y - 1)),
]
|> filter(fn(p) {
let #(_, point) = p
areas |> contains(point) |> bool.negate
})
}
fn count_contiguous(bounds: List(#(Bound, #(Int, Int)))) -> Int {
bounds
|> list.count(fn(b) {
case b {
#(Top, #(x, y)) -> bounds |> contains(#(Top, #(x - 1, y)))
#(Bottom, #(x, y)) -> bounds |> contains(#(Bottom, #(x - 1, y)))
#(Left, #(x, y)) -> bounds |> contains(#(Left, #(x, y - 1)))
#(Right, #(x, y)) -> bounds |> contains(#(Right, #(x, y - 1)))
}
|> bool.negate
})
}
fn detect_regions(textmap: Dict(#(Int, Int), String)) -> List(List(#(Int, Int))) {
textmap
|> dict.keys
|> fold(#(textmap, []), fn(acc, x) {
let #(searchspace, lists) = acc
// Make sure we haven't already included this key in another region
case lists |> list.find(fn(lst) { lst |> contains(x) }) {
Ok(_) -> acc
_ -> {
let assert Ok(plant) = dict.get(textmap, x)
find_region(searchspace, x, plant, [])
|> pair.map_second(fn(lst) { [lst |> list.unique, ..lists] })
}
}
})
|> pair.second
}
fn find_region(
textmap: Dict(#(Int, Int), String),
start: #(Int, Int),
plant: String,
processed: List(#(Int, Int)),
) -> #(Dict(#(Int, Int), String), List(#(Int, Int))) {
let #(x, y) = start
let searchspace = dict.delete(textmap, start)
[#(x - 1, y), #(x + 1, y), #(x, y - 1), #(x, y + 1)]
|> filter(fn(x) { is_same(textmap, x, plant) })
|> fold(#(searchspace, [start, ..processed]), fn(acc, x) {
let #(partialmap, current) = acc
find_region(partialmap, x, plant, current)
})
}
fn is_same(
textmap: Dict(#(Int, Int), String),
point: #(Int, Int),
plant: String,
) -> Bool {
case dict.get(textmap, point) {
Ok(s) -> s == plant
_ -> False
}
}
fn read_input(it: iterator.Iterator(String)) -> Dict(#(Int, Int), String) {
it
|> iterator.fold([], fn(acc, line) {
case string.trim(line) {
"" -> acc
str -> {
[str |> string.to_graphemes, ..acc]
}
}
})
|> reverse
|> list.index_fold(dict.new(), fn(acc, line, y) {
line
|> list.index_fold(acc, fn(dic, num, x) { dict.insert(dic, #(x, y), num) })
})
}