diff --git a/src/resonant_collinearity.gleam b/src/resonant_collinearity.gleam index c0a4619..6ec1675 100644 --- a/src/resonant_collinearity.gleam +++ b/src/resonant_collinearity.gleam @@ -6,7 +6,7 @@ import gleam/result import gleam/string type Point = - #(Int, Int) + #(Float, Float) type Grid = Dict(Point, String) @@ -18,7 +18,7 @@ type Direction { fn parse_column(line, y) { use grid, char, x <- list.index_fold(line, dict.new()) - dict.insert(grid, #(x, y), char) + dict.insert(grid, #(int.to_float(x), int.to_float(y)), char) } fn square(input: Float) -> Float { @@ -35,110 +35,76 @@ fn find_collinear_vector( 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) - let assert Ok(distance) = - float.add( - square(float.subtract(x2f, x1f)), - square(float.subtract(y2f, y1f)), - ) + float.add(square(float.subtract(x2, x1)), square(float.subtract(y2, y1))) |> float.square_root - let assert Ok(dx) = float.divide(float.subtract(x2f, x1f), distance) - let assert Ok(dy) = float.divide(float.subtract(y2f, y1f), distance) + 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( - point_a: Point, - point_b: Point, + 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 - 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.add(x2f, float.multiply(dx, distance))), - float.round(float.add(y2f, float.multiply(dy, distance))), + float.ceiling(float.add(x2, float.multiply(dx, distance))), + float.ceiling(float.add(y2, float.multiply(dy, distance))), ) Reverse -> #( - float.round(float.subtract(x1f, float.multiply(dx, distance))), - float.round(float.subtract(y1f, float.multiply(dy, distance))), + float.ceiling(float.subtract(x1, float.multiply(dx, distance))), + float.ceiling(float.subtract(y1, float.multiply(dy, distance))), ) } } -fn find_forward_harmonic_collinears( - found: List(Point), - grid: Grid, -) -> List(Point) { - let #(head, rest) = list.split(found, 2) - let assert [p1, p2] = head - let collinear_in_direction = find_collinear_in_direction(p1, p2, Forward) - - case point_within_map(collinear_in_direction, grid) { - True -> - find_forward_harmonic_collinears( - list.append([p2, collinear_in_direction], list.append(rest, [p1])), - grid, - ) - False -> found - } -} - -fn find_reverse_harmonic_collinears( +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(p1, p2, Reverse) + let collinear_in_direction = find_collinear_in_direction(head, direction) case point_within_map(collinear_in_direction, grid) { True -> - find_reverse_harmonic_collinears( - list.append([collinear_in_direction, p1], list.append(rest, [p2])), - grid, - ) + 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( - point_a: Point, - point_b: Point, - grid: Grid, -) -> List(Point) { +fn find_all_harmonic_collinears(points: List(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), + find_harmonic_collinear_in_direction(points, grid, Forward), + find_harmonic_collinear_in_direction(points, grid, 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 { - case cell { - "." -> False - _ -> True - } +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) { @@ -152,12 +118,7 @@ fn point_within_map(point: Point, g: Grid) -> Bool { fn 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_collinears(l1, l2) - _ -> [] - } - }) + |> list.flat_map(find_all_collinears) |> list.filter(point_within_map(_, g)) |> list.unique } @@ -165,14 +126,8 @@ fn collinears_for_antenna(antenna: String, g: Grid) -> List(Point) { 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) - _ -> [] - } - }) + |> list.flat_map(find_all_harmonic_collinears(_, g)) |> list.filter(point_within_map(_, g)) - |> list.unique } fn parse_input(input: String) -> Grid { @@ -188,7 +143,7 @@ fn parse_input(input: String) -> Grid { pub fn solve_a(input: String) -> Int { let grid = parse_input(input) - list.filter(list.unique(dict.values(grid)), is_not_empty) + list.filter(list.unique(dict.values(grid)), fn(x) { x != "." }) |> list.map(collinears_for_antenna(_, grid)) |> list.flatten |> list.unique @@ -198,7 +153,7 @@ pub fn solve_a(input: String) -> Int { pub fn solve_b(input: String) -> Int { let grid = parse_input(input) - list.filter(list.unique(dict.values(grid)), is_not_empty) + list.filter(list.unique(dict.values(grid)), fn(x) { x != "." }) |> list.map(harmonic_collinears_for_antenna(_, grid)) |> list.flatten |> list.unique