diff options
author | J.J <thechairman@thechairman.info> | 2023-12-03 12:20:36 -0500 |
---|---|---|
committer | J.J <thechairman@thechairman.info> | 2023-12-03 12:20:36 -0500 |
commit | 03a8465fef75336989ff7d20050b643e541e5a36 (patch) | |
tree | e30d983f8a532d86de22a08fd2f3fb77bc66bfd8 | |
parent | 0607a032238b545d3d49d3af0e1763f82ccdd31a (diff) | |
parent | 8a521c9df766b733f03d357340f74935c65f7de7 (diff) | |
download | gleam_aoc-03a8465fef75336989ff7d20050b643e541e5a36.tar.gz gleam_aoc-03a8465fef75336989ff7d20050b643e541e5a36.zip |
Merge branch 'main' of https://github.com/hunkyjimpjorps/AdventOfCode
-rw-r--r-- | aoc2023-other/day-02/day-02-parser.rkt | 44 | ||||
-rw-r--r-- | aoc2023-other/day-03/day-03.rkt | 68 | ||||
-rw-r--r-- | aoc2023/src/day3/.gitignore | 1 | ||||
-rw-r--r-- | aoc2023/src/day3/solve.gleam | 178 | ||||
-rw-r--r-- | aoc2023/test/day3/day3_test.gleam | 66 |
5 files changed, 340 insertions, 17 deletions
diff --git a/aoc2023-other/day-02/day-02-parser.rkt b/aoc2023-other/day-02/day-02-parser.rkt index 67f5ae6..76cc24f 100644 --- a/aoc2023-other/day-02/day-02-parser.rkt +++ b/aoc2023-other/day-02/day-02-parser.rkt @@ -13,33 +13,43 @@ (define cube/p (do [n <- integer/p] - space/p - [c <- (or/p (string/p "red") (string/p "blue") (string/p "green"))] - (pure (cons c n)))) + space/p + [c <- (or/p (string/p "red") + (string/p "blue") + (string/p "green"))] + (pure (cons c n)))) (define draw/p - (do [xs <- (many/p cube/p #:min 1 #:max 3 #:sep (string/p ", "))] (pure (apply hash (flatten xs))))) + (do [xs <- (many/p cube/p #:min 1 #:max 3 #:sep (string/p ", "))] + (pure (apply hash (flatten xs))))) (define all-draws/p (do (string/p "Game ") - [id <- integer/p] - (string/p ": ") - [all-draws <- (many/p draw/p #:min 1 #:sep (string/p "; "))] - (define maxima (max-cubes all-draws)) - (pure (game id (hash-ref maxima "red") (hash-ref maxima "green") (hash-ref maxima "blue"))))) - -(define (max-cubes h) - (foldl (curry hash-union #:combine max) (hash "red" 0 "green" 0 "blue" 0) h)) + [id <- integer/p] + (string/p ": ") + [all-draws <- (many/p draw/p #:min 1 #:sep (string/p "; "))] + (define maxima + (foldl (curry hash-union #:combine max) + (hash "red" 0 "green" 0 "blue" 0) + all-draws)) + (pure (game id + (hash-ref maxima "red") + (hash-ref maxima "green") + (hash-ref maxima "blue"))))) (define game-maxima (~>> (open-aoc-input (find-session) 2023 2) port->lines - (map (λ~>> (parse-string all-draws/p) from-either)))) + (map (λ~>> (parse-string all-draws/p) + from-either)))) ;; part 1 -(for/sum ([m (in-list game-maxima)] #:unless - (or (> (game-r m) 12) (> (game-g m) 13) (> (game-b m) 14))) - (game-id m)) +(for/sum ([m (in-list game-maxima)] + #:unless (or (> (game-r m) 12) + (> (game-g m) 13) + (> (game-b m) 14))) + (game-id m)) ;; part 2 -(for/sum ([m (in-list game-maxima)]) (* (game-r m) (game-g m) (game-b m))) +(for/sum ([m (in-list game-maxima)]) + (* (game-r m) (game-g m) (game-b m))) diff --git a/aoc2023-other/day-03/day-03.rkt b/aoc2023-other/day-03/day-03.rkt new file mode 100644 index 0000000..d52b11b --- /dev/null +++ b/aoc2023-other/day-03/day-03.rkt @@ -0,0 +1,68 @@ +#lang racket + +(require advent-of-code + threading) + +(struct posn (x y) #:transparent) +(struct part (n posns) #:transparent) + +(define (make-board port) + (for*/hash ([(row y) (in-indexed (port->lines port))] + [(col x) (in-indexed (string->list row))] + #:unless (equal? col #\.)) + (define v + (cond + [(string->number (string col))] + [(equal? col #\*) 'gear] + [else 'other])) + (values (posn x y) v))) + +(define board (~> (open-aoc-input (find-session) 2023 3 #:cache #true) make-board)) + +(define (posn<? a b) + (match-define (list (cons (posn a-x a-y) _) (cons (posn b-x b-y) _)) (list a b)) + (if (= a-y b-y) (< a-x b-x) (< a-y b-y))) + +(define (find-cells f b) + (~> (for/hash ([(k v) (in-hash b)] #:when (f v)) + (values k v)) + hash->list + (sort posn<?))) + +(define (group-into-parts cells [acc '()]) + (match* (cells acc) + [('() acc) acc] + [((list* (cons (and pt (posn x y)) n) cs) (list* (part n* (and pts (list* (posn x* y) rest-pts))) + rest-acc)) + #:when (= (- x x*) 1) + (group-into-parts cs (cons (part (+ n (* n* 10)) (cons pt pts)) rest-acc))] + [((list* (cons pt n) cs) acc) (group-into-parts cs (cons (part n (list pt)) acc))])) + +(define (neighbors p) + (for*/list ([dx '(-1 0 1)] [dy '(-1 0 1)] #:unless (and (= dx 0) (= dy 0))) + (posn (+ dx (posn-x p)) (+ dy (posn-y p))))) + +(define to-neighbors (λ~>> part-posns (append-map neighbors) remove-duplicates)) +(define (symbol-in-neighbors b pt acc) + (~>> pt + to-neighbors + (ormap (λ (p) + (let ([lookup (hash-ref b p #f)]) (or (equal? lookup 'gear) (equal? lookup 'other))))) + ((λ (bool) (if bool (+ acc (part-n pt)) acc))))) + +;; part 1 +(define parts (~>> board (find-cells integer?) group-into-parts)) +(foldl (curry symbol-in-neighbors board) 0 parts) + +;; part 2 +(define gears (~>> board (find-cells (curry equal? 'gear)) (map car))) +(define parts-with-neighbors + (~>> parts (map (λ (pt) (struct-copy part pt [posns (to-neighbors pt)]))))) + +(define (find-parts-near-gear pts gear) + (filter-map (λ (pt) (and (findf (curry equal? gear) (part-posns pt)) (part-n pt))) pts)) + +(~>> gears + (filter-map (λ~>> (find-parts-near-gear parts-with-neighbors) + ((λ (ns) (if (= (length ns) 2) (* (first ns) (second ns)) #f))))) + (apply +)) 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) +} |