Skip to content

Commit

Permalink
day 08: solved
Browse files Browse the repository at this point in the history
  • Loading branch information
sreedevk committed Dec 8, 2024
1 parent e7d1ed1 commit 532ff18
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 0 deletions.
15 changes: 15 additions & 0 deletions src/advent_of_code.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import historian_hysteria as day01
import mull_it_over as day03
import print_queue as day05
import red_nosed_reports as day02
import resonant_collinearity as day08
import simplifile.{read}

pub fn main() {
Expand Down Expand Up @@ -110,6 +111,20 @@ pub fn main() {
Nil,
)
}

["8"] -> {
result.unwrap(
result.map(read("data/day8.txt"), fn(data) {
io.println(
"[8] Bridge Repair (Part 1): " <> int.to_string(day08.solve_a(data)),
)
io.println(
"[8] Bridge Repair (Part 2): " <> int.to_string(day08.solve_b(data)),
)
}),
Nil,
)
}
_ -> io.println_error("invalid arguments!")
}
}
160 changes: 160 additions & 0 deletions src/resonant_collinearity.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import gleam/dict.{type Dict}
import gleam/float
import gleam/int
import gleam/list
import gleam/result
import gleam/string

type Point =
#(Float, Float)

type Grid =
Dict(Point, String)

type Direction {
Forward
Reverse
}

fn parse_column(line, y) {
use grid, char, x <- list.index_fold(line, dict.new())
dict.insert(grid, #(int.to_float(x), int.to_float(y)), char)
}

fn square(input: Float) -> Float {
case float.power(input, 2.0) {
Ok(val) -> val
_ -> 0.0
}
}

fn find_collinear_vector(
point_a: Point,
point_b: Point,
) -> #(Float, #(Float, Float)) {
let #(x1, y1) = point_a
let #(x2, y2) = point_b

let assert Ok(distance) =
float.add(square(float.subtract(x2, x1)), square(float.subtract(y2, y1)))
|> float.square_root

let assert Ok(dx) = float.divide(float.subtract(x2, x1), distance)
let assert Ok(dy) = float.divide(float.subtract(y2, y1), distance)

#(distance, #(dx, dy))
}

fn find_collinear_in_direction(
points: List(Point),
direction: Direction,
) -> Point {
let assert [point_a, point_b] = points
let #(distance, #(dx, dy)) = find_collinear_vector(point_a, point_b)
let #(x1, y1) = point_a
let #(x2, y2) = point_b

case direction {
Forward -> #(
float.ceiling(float.add(x2, float.multiply(dx, distance))),
float.ceiling(float.add(y2, float.multiply(dy, distance))),
)
Reverse -> #(
float.ceiling(float.subtract(x1, float.multiply(dx, distance))),
float.ceiling(float.subtract(y1, float.multiply(dy, distance))),
)
}
}

fn find_harmonic_collinear_in_direction(
found: List(Point),
grid: Grid,
direction: Direction,
) -> List(Point) {
let #(head, rest) = list.split(found, 2)
let assert [p1, p2] = head
let collinear_in_direction = find_collinear_in_direction(head, direction)

case point_within_map(collinear_in_direction, grid) {
True ->
case direction {
Forward ->
find_harmonic_collinear_in_direction(
list.append([p2, collinear_in_direction], list.append(rest, [p1])),
grid,
direction,
)
Reverse ->
find_harmonic_collinear_in_direction(
list.append([collinear_in_direction, p1], list.append(rest, [p2])),
grid,
direction,
)
}
False -> found
}
}

fn find_all_harmonic_collinears(points: List(Point), grid: Grid) -> List(Point) {
list.append(
find_harmonic_collinear_in_direction(points, grid, Forward),
find_harmonic_collinear_in_direction(points, grid, Reverse),
)
}

fn find_all_collinears(points: List(Point)) -> List(Point) {
list.wrap(find_collinear_in_direction(points, Forward))
|> list.append(list.wrap(find_collinear_in_direction(points, Reverse)))
}

fn find_antenna_locations(antenna: String, grid: Grid) -> List(Point) {
dict.keys(dict.filter(grid, fn(_, ant) { ant == antenna }))
}

fn point_within_map(point: Point, g: Grid) -> Bool {
result.is_ok(list.find(dict.keys(g), fn(x) { x == point }))
}

fn collinears_for_antenna(antenna: String, g: Grid) -> List(Point) {
find_antenna_locations(antenna, g)
|> list.combinations(2)
|> list.flat_map(find_all_collinears)
|> list.filter(point_within_map(_, g))
|> list.unique
}

fn harmonic_collinears_for_antenna(antenna: String, g: Grid) -> List(Point) {
find_antenna_locations(antenna, g)
|> list.combinations(2)
|> list.flat_map(find_all_harmonic_collinears(_, g))
|> list.filter(point_within_map(_, g))
}

fn parse_input(input: String) -> Grid {
input
|> string.trim()
|> string.split("\n")
|> list.map(string.to_graphemes)
|> list.index_map(parse_column)
|> list.fold(dict.new(), dict.merge)
}

pub fn solve_a(input: String) -> Int {
let grid = parse_input(input)

list.filter(list.unique(dict.values(grid)), fn(x) { x != "." })
|> list.map(collinears_for_antenna(_, grid))
|> list.flatten
|> list.unique
|> list.length
}

pub fn solve_b(input: String) -> Int {
let grid = parse_input(input)

list.filter(list.unique(dict.values(grid)), fn(x) { x != "." })
|> list.map(harmonic_collinears_for_antenna(_, grid))
|> list.flatten
|> list.unique
|> list.length
}
22 changes: 22 additions & 0 deletions test/resonant_collinearity_test.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import gleam/string
import gleeunit/should
import resonant_collinearity as day08

fn test_data() -> String {
string.join(
[
"............", "........0...", ".....0......", ".......0....",
"....0.......", "......A.....", "............", "............",
"........A...", ".........A..", "............", "............",
],
"\n",
)
}

pub fn solve_a_test() {
should.equal(day08.solve_a(test_data()), 14)
}

pub fn solve_b_test() {
should.equal(day08.solve_b(test_data()), 34)
}

0 comments on commit 532ff18

Please sign in to comment.