diff --git a/src/resonant_collinearity.gleam b/src/resonant_collinearity.gleam index 0f38d84..ac94881 100644 --- a/src/resonant_collinearity.gleam +++ b/src/resonant_collinearity.gleam @@ -12,6 +12,11 @@ type Point = 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, #(x, y), char) @@ -24,7 +29,10 @@ fn square(input: Float) -> Float { } } -fn find_collinears(point_a: Point, point_b: Point) -> List(Point) { +fn find_collinear_vector( + point_a: Point, + point_b: Point, +) -> #(Float, #(Float, Float)) { let #(x1, y1) = point_a let #(x2, y2) = point_b @@ -43,18 +51,96 @@ fn find_collinears(point_a: Point, point_b: Point) -> List(Point) { let assert Ok(dx) = float.divide(float.subtract(x2f, x1f), distance) let assert Ok(dy) = float.divide(float.subtract(y2f, y1f), distance) - let x3f_forward = float.add(x2f, float.multiply(dx, distance)) - let y3f_forward = float.add(y2f, float.multiply(dy, distance)) - let x3f_reverse = float.subtract(x1f, float.multiply(dx, distance)) - let y3f_reverse = float.subtract(y1f, float.multiply(dy, distance)) + #(distance, #(dx, dy)) +} + +fn find_collinear_in_direction( + point_a: Point, + point_b: Point, + direction: Direction, +) -> Point { + let #(distance, #(dx, dy)) = find_collinear_vector(point_a, point_b) + let #(x1, y1) = point_a + let #(x2, y2) = point_b + + let x1f = int.to_float(x1) + let y1f = int.to_float(y1) + + let x2f = int.to_float(x2) + let y2f = int.to_float(y2) + + case direction { + Forward -> #( + float.round(float.subtract(x1f, float.multiply(dx, distance))), + float.round(float.subtract(y1f, float.multiply(dy, distance))), + ) + Reverse -> #( + float.round(float.add(x2f, float.multiply(dx, distance))), + float.round(float.add(y2f, float.multiply(dy, distance))), + ) + } +} + +fn points_within_map(points: List(Point), grid: Grid) -> Bool { + list.all(points, point_within_map(_, grid)) +} + +fn find_forward_harmonic_collinears( + found: List(Point), + grid: Grid, +) -> List(Point) { + let #(head, _rest) = list.split(found, 2) + let assert [p1, p2] = head + case points_within_map(head, grid) { + True -> { + find_forward_harmonic_collinears( + list.append( + list.wrap(find_collinear_in_direction(p1, p2, Forward)), + found, + ), + grid, + ) + } + False -> found + } +} - let x3_forward = float.round(x3f_forward) - let y3_forward = float.round(y3f_forward) +fn find_reverse_harmonic_collinears( + found: List(Point), + grid: Grid, +) -> List(Point) { + let #(head, _rest) = list.split(found, 2) + let assert [p1, p2] = head + case points_within_map(head, grid) { + True -> { + find_reverse_harmonic_collinears( + list.append( + list.wrap(find_collinear_in_direction(p1, p2, Reverse)), + found, + ), + grid, + ) + } + False -> found + } +} - let x3_reverse = float.round(x3f_reverse) - let y3_reverse = float.round(y3f_reverse) +fn find_all_harmonic_collinears( + point_a: Point, + point_b: Point, + grid: Grid, +) -> List(Point) { + list.append( + find_forward_harmonic_collinears([point_a, point_b], grid), + find_reverse_harmonic_collinears([point_a, point_b], grid), + ) +} - [#(x3_forward, y3_forward), #(x3_reverse, y3_reverse)] +fn find_all_collinears(point_a: Point, point_b: Point) -> List(Point) { + [ + find_collinear_in_direction(point_a, point_b, Forward), + find_collinear_in_direction(point_a, point_b, Reverse), + ] } fn is_not_empty(cell: String) -> Bool { @@ -77,7 +163,20 @@ fn collinears_for_antenna(antenna: String, g: Grid) -> List(Point) { |> list.combinations(2) |> list.flat_map(fn(points) { case points { - [l1, l2] -> find_collinears(l1, l2) + [l1, l2] -> find_all_collinears(l1, l2) + _ -> [] + } + }) + |> 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(fn(points) { + case points { + [l1, l2] -> find_all_harmonic_collinears(l1, l2, g) _ -> [] } }) @@ -105,6 +204,12 @@ pub fn solve_a(input: String) -> Int { |> list.length } -pub fn solve_b(_inp: String) -> Int { - 0 +pub fn solve_b(input: String) -> Int { + let grid = parse_input(input) + + list.filter(list.unique(dict.values(grid)), is_not_empty) + |> list.map(harmonic_collinears_for_antenna(_, grid)) + |> list.flatten + |> list.unique + |> list.length } diff --git a/test/resonant_collinearity_test.gleam b/test/resonant_collinearity_test.gleam index 9c73c27..57f2d9d 100644 --- a/test/resonant_collinearity_test.gleam +++ b/test/resonant_collinearity_test.gleam @@ -18,5 +18,5 @@ pub fn solve_a_test() { } pub fn solve_b_test() { - should.equal(day08.solve_b(test_data()), 0) + should.equal(day08.solve_b(test_data()), 34) }