|
|
@ -0,0 +1,62 @@ |
|
|
|
|
|
-module(day15). |
|
|
|
|
|
|
|
|
|
|
|
-export([solve/1]). |
|
|
|
|
|
|
|
|
|
|
|
solve(Input) -> |
|
|
|
|
|
{part1(Input), none}. |
|
|
|
|
|
|
|
|
|
|
|
part1(Input) -> |
|
|
|
|
|
Sensors = parse_sensors(Input), |
|
|
|
|
|
Ranges = get_row(Sensors, 2000000), |
|
|
|
|
|
sum_ranges(Ranges). |
|
|
|
|
|
|
|
|
|
|
|
parse_sensors(Input) -> |
|
|
|
|
|
lists:map(fun parse_sensor/1, binary:split(Input, <<$\n>>, [global, trim_all])). |
|
|
|
|
|
parse_sensor(Line) -> |
|
|
|
|
|
{ok, [SensorX, SensorY, BeaconX, BeaconY], _Rest} = io_lib:fread("Sensor at x=~d, y=~d: closest beacon is at x=~d, y=~d", binary_to_list(Line)), |
|
|
|
|
|
{{SensorX, SensorY}, {BeaconX, BeaconY}}. |
|
|
|
|
|
|
|
|
|
|
|
get_row(Sensors, Row) -> |
|
|
|
|
|
remove_beacons(Sensors, Row, get_row(Sensors, Row, rset_new())). |
|
|
|
|
|
|
|
|
|
|
|
remove_beacons([], _Row, Set) -> Set; |
|
|
|
|
|
remove_beacons([{_, {BeaconX, Row}}|Rest], Row, Set) -> remove_beacons(Rest, Row, rset_remove1(BeaconX, Set)); |
|
|
|
|
|
remove_beacons([_|Rest], Row, Set) -> remove_beacons(Rest, Row, Set). |
|
|
|
|
|
|
|
|
|
|
|
get_row([], _Row, Acc) -> Acc; |
|
|
|
|
|
get_row([Sensor | Rest], Row, Acc) -> |
|
|
|
|
|
NewAcc = |
|
|
|
|
|
case get_intersection(Sensor, Row) of |
|
|
|
|
|
none -> Acc; |
|
|
|
|
|
Range -> rset_append(Range, Acc) |
|
|
|
|
|
end, |
|
|
|
|
|
get_row(Rest, Row, NewAcc). |
|
|
|
|
|
|
|
|
|
|
|
get_intersection({{SensorX, SensorY} = Sensor, NearestBeacon}, Row) -> |
|
|
|
|
|
case vec_mag(vec_sub(Sensor, NearestBeacon)) - abs(Row - SensorY) of |
|
|
|
|
|
TooFar when TooFar < 0 -> none; |
|
|
|
|
|
Signal -> {SensorX - Signal, SensorX + Signal} |
|
|
|
|
|
end. |
|
|
|
|
|
|
|
|
|
|
|
vec_sub({LX, LY}, {RX, RY}) -> {LX - RX, LY - RY}. |
|
|
|
|
|
% vector magnitude |
|
|
|
|
|
vec_mag({X, Y}) -> abs(X) + abs(Y). |
|
|
|
|
|
|
|
|
|
|
|
sum_ranges([]) -> 0; |
|
|
|
|
|
sum_ranges([{Start, End} | Rest]) -> End - Start + 1 + sum_ranges(Rest). |
|
|
|
|
|
|
|
|
|
|
|
rset_new() -> []. |
|
|
|
|
|
|
|
|
|
|
|
% rangeset is a sorted set of discrete ranges. Adjacent ranges are merged. |
|
|
|
|
|
rset_append(Entry, []) -> [Entry]; |
|
|
|
|
|
rset_append({_, End} = Entry, [{HeadStart, _} | _] = Set) when End < HeadStart - 1 -> [Entry | Set]; |
|
|
|
|
|
rset_append({Start, _} = Entry, [{_, HeadEnd} = Head | Rest]) when Start > HeadEnd + 1 -> [ Head | rset_append(Entry, Rest)]; |
|
|
|
|
|
rset_append({Start, End}, [{HeadStart, HeadEnd} | Rest]) -> rset_append({min(Start, HeadStart), max(End, HeadEnd)}, Rest). |
|
|
|
|
|
|
|
|
|
|
|
rset_remove1(_Point, []) -> []; |
|
|
|
|
|
rset_remove1(Point, [{Start, _End} | _Rest] = Set) when Point < Start -> Set; |
|
|
|
|
|
rset_remove1(Point, [{_Start, End} = Head | Rest]) when Point > End -> [Head | rset_remove1(Point, Rest)]; |
|
|
|
|
|
rset_remove1(Point, [{Point, Point} | Rest]) -> Rest; |
|
|
|
|
|
rset_remove1(Point, [{Point, End} | Rest]) -> [{Point+1, End} | Rest]; |
|
|
|
|
|
rset_remove1(Point, [{Start, Point} | Rest]) -> [{Start, Point-1} | Rest]; |
|
|
|
|
|
rset_remove1(Point, [{Start, End} | Rest]) -> [{Start, Point-1}, {Point+1, End} | Rest]. |