aboutsummaryrefslogtreecommitdiff
path: root/aoc2023-gleam/src/day3/solve.gleam
diff options
context:
space:
mode:
authorHunky Jimpjorps <thechairman@thechairman.info>2024-02-02 17:05:12 -0500
committerHunky Jimpjorps <thechairman@thechairman.info>2024-02-02 17:05:12 -0500
commit48e35ad3b0b0c62f936784e4aca70b17c3b0e3f9 (patch)
treef59a13e0b5e80ab925220b4488c6e36b1bec660a /aoc2023-gleam/src/day3/solve.gleam
parent87e9ab25ff70e215b537939a4bc23ab101f41dbe (diff)
downloadgleam_aoc-48e35ad3b0b0c62f936784e4aca70b17c3b0e3f9.tar.gz
gleam_aoc-48e35ad3b0b0c62f936784e4aca70b17c3b0e3f9.zip
renaming
Diffstat (limited to 'aoc2023-gleam/src/day3/solve.gleam')
-rw-r--r--aoc2023-gleam/src/day3/solve.gleam180
1 files changed, 180 insertions, 0 deletions
diff --git a/aoc2023-gleam/src/day3/solve.gleam b/aoc2023-gleam/src/day3/solve.gleam
new file mode 100644
index 0000000..ad975aa
--- /dev/null
+++ b/aoc2023-gleam/src/day3/solve.gleam
@@ -0,0 +1,180 @@
+import adglent.{First, Second}
+import gleam/io
+import gleam/dict.{type Dict}
+import gleam/string
+import gleam/list
+import gleam/int
+import gleam/order.{type Order, Eq}
+
+type Coord {
+ Coord(x: Int, y: Int)
+}
+
+type SymbolKind {
+ Gear
+ SomethingElse
+}
+
+type Symbol {
+ Number(Int)
+ Symbol(SymbolKind)
+ Empty
+}
+
+type Board =
+ Dict(Coord, Symbol)
+
+type Cell {
+ Cell(coord: Coord, symbol: Symbol)
+}
+
+type Part {
+ Part(coords: List(Coord), part_number: Int)
+}
+
+fn to_symbol(c: String) -> Symbol {
+ case int.parse(c), c {
+ Ok(n), _ -> Number(n)
+ _, "." -> Empty
+ _, "*" -> Symbol(Gear)
+ _, _ -> Symbol(SomethingElse)
+ }
+}
+
+fn to_board(input: String) -> Board {
+ {
+ use y, r <- list.index_map(string.split(input, "\n"))
+ use x, c <- list.index_map(string.to_graphemes(r))
+ #(Coord(x, y), to_symbol(c))
+ }
+ |> list.flatten()
+ |> dict.from_list()
+}
+
+fn cell_compare(a: Cell, b: Cell) -> Order {
+ case int.compare(a.coord.y, b.coord.y) {
+ Eq -> int.compare(a.coord.x, b.coord.x)
+ other -> other
+ }
+}
+
+fn find_all_part_digits(b: Board) -> List(Cell) {
+ b
+ |> dict.filter(fn(_, v) {
+ case v {
+ Number(_) -> True
+ _ -> False
+ }
+ })
+ |> dict.to_list()
+ |> list.map(fn(tup) { Cell(tup.0, tup.1) })
+ |> list.sort(cell_compare)
+}
+
+fn to_parts(cells: List(Cell)) -> List(Part) {
+ do_parts(cells, [])
+}
+
+fn do_parts(cells: List(Cell), parts: List(Part)) -> List(Part) {
+ case cells {
+ [] -> parts
+ [Cell(next, Number(n)), ..t] -> {
+ case parts {
+ [] -> do_parts(t, [Part([next], n), ..parts])
+ [Part([prev, ..] as coords, n0), ..rest_parts] ->
+ case { next.x - prev.x }, { next.y - prev.y } {
+ 1, 0 ->
+ do_parts(t, [Part([next, ..coords], n0 * 10 + n), ..rest_parts])
+ _, _ -> do_parts(t, [Part([next], n), ..parts])
+ }
+ _ -> panic
+ }
+ }
+ _ -> panic
+ }
+}
+
+fn all_neighbors(c: Coord) -> List(Coord) {
+ use dx <- list.flat_map([-1, 0, 1])
+ use dy <- list.filter_map([-1, 0, 1])
+ case dx, dy {
+ 0, 0 -> Error(Nil)
+ _, _ -> Ok(Coord(c.x + dx, c.y + dy))
+ }
+}
+
+fn sum_valid_parts(acc: Int, part: Part, board: Board) -> Int {
+ let neighbors =
+ part.coords
+ |> list.flat_map(all_neighbors)
+ |> list.unique()
+
+ let sym = [Ok(Symbol(Gear)), Ok(Symbol(SomethingElse))]
+ case list.any(neighbors, fn(c) { list.contains(sym, dict.get(board, c)) }) {
+ True -> acc + part.part_number
+ False -> acc
+ }
+}
+
+pub fn part1(input: String) -> Int {
+ let board = to_board(input)
+
+ board
+ |> find_all_part_digits
+ |> to_parts
+ |> list.fold(0, fn(acc, p) { sum_valid_parts(acc, p, board) })
+}
+
+fn to_part_with_neighbors(part: Part) -> Part {
+ part.coords
+ |> list.flat_map(all_neighbors)
+ |> list.unique
+ |> Part(part.part_number)
+}
+
+fn find_part_numbers_near_gear(gear: Coord, parts: List(Part)) -> List(Int) {
+ use part <- list.filter_map(parts)
+ case list.contains(part.coords, gear) {
+ True -> Ok(part.part_number)
+ False -> Error(Nil)
+ }
+}
+
+fn to_sum_of_gear_ratios(adjacent_parts: List(List(Int))) -> Int {
+ use acc, ps <- list.fold(adjacent_parts, 0)
+ case ps {
+ [p1, p2] -> acc + p1 * p2
+ _ -> acc
+ }
+}
+
+pub fn part2(input: String) -> Int {
+ let board = to_board(input)
+
+ let parts =
+ board
+ |> find_all_part_digits
+ |> to_parts
+ |> list.map(to_part_with_neighbors)
+
+ board
+ |> dict.filter(fn(_, v) { v == Symbol(Gear) })
+ |> dict.keys
+ |> list.map(find_part_numbers_near_gear(_, parts))
+ |> to_sum_of_gear_ratios
+}
+
+pub fn main() {
+ let assert Ok(part) = adglent.get_part()
+ let assert Ok(input) = adglent.get_input("3")
+ case part {
+ First ->
+ part1(input)
+ |> adglent.inspect
+ |> io.println
+ Second ->
+ part2(input)
+ |> adglent.inspect
+ |> io.println
+ }
+}