aboutsummaryrefslogtreecommitdiff
path: root/aoc2023
diff options
context:
space:
mode:
authorJ.J <thechairman@thechairman.info>2023-12-03 12:20:36 -0500
committerJ.J <thechairman@thechairman.info>2023-12-03 12:20:36 -0500
commit03a8465fef75336989ff7d20050b643e541e5a36 (patch)
treee30d983f8a532d86de22a08fd2f3fb77bc66bfd8 /aoc2023
parent0607a032238b545d3d49d3af0e1763f82ccdd31a (diff)
parent8a521c9df766b733f03d357340f74935c65f7de7 (diff)
downloadgleam_aoc-03a8465fef75336989ff7d20050b643e541e5a36.tar.gz
gleam_aoc-03a8465fef75336989ff7d20050b643e541e5a36.zip
Merge branch 'main' of https://github.com/hunkyjimpjorps/AdventOfCode
Diffstat (limited to 'aoc2023')
-rw-r--r--aoc2023/src/day3/.gitignore1
-rw-r--r--aoc2023/src/day3/solve.gleam178
-rw-r--r--aoc2023/test/day3/day3_test.gleam66
3 files changed, 245 insertions, 0 deletions
diff --git a/aoc2023/src/day3/.gitignore b/aoc2023/src/day3/.gitignore
new file mode 100644
index 0000000..ae40cea
--- /dev/null
+++ b/aoc2023/src/day3/.gitignore
@@ -0,0 +1 @@
+input.txt \ No newline at end of file
diff --git a/aoc2023/src/day3/solve.gleam b/aoc2023/src/day3/solve.gleam
new file mode 100644
index 0000000..fa16bb8
--- /dev/null
+++ b/aoc2023/src/day3/solve.gleam
@@ -0,0 +1,178 @@
+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])
+ }
+ }
+ }
+ }
+}
+
+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
+ }
+}
diff --git a/aoc2023/test/day3/day3_test.gleam b/aoc2023/test/day3/day3_test.gleam
new file mode 100644
index 0000000..30e17a9
--- /dev/null
+++ b/aoc2023/test/day3/day3_test.gleam
@@ -0,0 +1,66 @@
+import gleam/list
+import showtime/tests/should
+import adglent.{type Example, Example}
+import day3/solve
+
+type Problem1AnswerType =
+ Int
+
+type Problem2AnswerType =
+ Int
+
+/// Add examples for part 1 here:
+/// ```gleam
+///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")]
+/// ```
+const part1_examples: List(Example(Problem1AnswerType)) = [
+ Example(
+ "467..114..
+...*......
+..35..633.
+......#...
+617*......
+.....+.58.
+..592.....
+......755.
+...$.*....
+.664.598..",
+ 4361,
+ ),
+]
+
+/// Add examples for part 2 here:
+/// ```gleam
+///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")]
+/// ```
+const part2_examples: List(Example(Problem2AnswerType)) = [
+ Example(
+ "467..114..
+...*......
+..35..633.
+......#...
+617*......
+.....+.58.
+..592.....
+......755.
+...$.*....
+.664.598..",
+ 467_835,
+ ),
+]
+
+pub fn part1_test() {
+ part1_examples
+ |> should.not_equal([])
+ use example <- list.map(part1_examples)
+ solve.part1(example.input)
+ |> should.equal(example.answer)
+}
+
+pub fn part2_test() {
+ part2_examples
+ |> should.not_equal([])
+ use example <- list.map(part2_examples)
+ solve.part2(example.input)
+ |> should.equal(example.answer)
+}