4 Commits
2742fabb80
...
e03ae75c7e
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
e03ae75c7e |
Day 15-1
|
3 years ago |
|
|
2df9608c6a |
Day 14-2
|
3 years ago |
|
|
2ff41f3c32 |
Day 14-1
|
3 years ago |
|
|
2a4a438c3d |
Day 13-2
|
3 years ago |
3 changed files with 180 additions and 3 deletions
@ -0,0 +1,104 @@ |
|||||
|
-module(day14). |
||||
|
|
||||
|
-export([solve/1]). |
||||
|
|
||||
|
solve(Input) -> |
||||
|
{part1(Input), part2(Input)}. |
||||
|
|
||||
|
|
||||
|
part1(Input) -> simulate(parse_map(Input), 0). |
||||
|
|
||||
|
part2(Input) -> |
||||
|
simulate(parse_map2(Input), 0). |
||||
|
|
||||
|
parse_map2(Input) -> |
||||
|
LineSegments = parse_instructions(Input), |
||||
|
FloorDepth = y_max(LineSegments, 0) + 2, |
||||
|
FlooredLineSegments = [{{500 - FloorDepth, FloorDepth}, {500 + FloorDepth, FloorDepth}} | LineSegments], |
||||
|
ChoppedSegments = chop_segments(FlooredLineSegments, []), |
||||
|
SegmentMap = maps:groups_from_list(fun({X, _, _}) -> X end, fun({_X, YStart, YEnd}) -> {YStart, YEnd} end, ChoppedSegments), |
||||
|
maps:map(fun (_Key, Segments) -> lists:foldl(fun rset_append/2, rset_new(), Segments) end, SegmentMap). |
||||
|
|
||||
|
y_max([], Max) -> Max; |
||||
|
y_max([{{_, Y1},{_,Y2}}|Rest], Max) -> y_max(Rest, max(max(Y1, Y2), Max)). |
||||
|
|
||||
|
parse_map(Input) -> |
||||
|
LineSegments = parse_instructions(Input), |
||||
|
ChoppedSegments = chop_segments(LineSegments, []), |
||||
|
SegmentMap = maps:groups_from_list(fun({X, _, _}) -> X end, fun({_X, YStart, YEnd}) -> {YStart, YEnd} end, ChoppedSegments), |
||||
|
maps:map(fun (_Key, Segments) -> lists:foldl(fun rset_append/2, rset_new(), Segments) end, SegmentMap). |
||||
|
|
||||
|
parse_instructions(Input) -> |
||||
|
lists:foldl(fun parse_instruction/2, [], binary:split(Input, <<$\n>>, [global, trim_all])). |
||||
|
|
||||
|
parse_instruction(Line, Acc) -> |
||||
|
{X1, <<$,, Rest0/binary>>} = parse_int(Line), |
||||
|
{Y1, Rest1} = parse_int(Rest0), |
||||
|
parse_instruction0(Rest1, {X1, Y1}, Acc). |
||||
|
|
||||
|
parse_instruction0(<<>>, _Point, Acc) -> Acc; |
||||
|
parse_instruction0(<<" -> ", RestIn/binary>>, Point, Acc) -> |
||||
|
{X, <<$,, Temp/binary>>} = parse_int(RestIn), |
||||
|
{Y, RestOut} = parse_int(Temp), |
||||
|
NewPoint = {X, Y}, |
||||
|
parse_instruction0(RestOut, NewPoint, [{Point, NewPoint}|Acc]). |
||||
|
|
||||
|
parse_int(<<$-, Rest/binary>>) -> |
||||
|
{AbsoluteValue, Binary} = parse_int0(Rest, 0), |
||||
|
{-1 * AbsoluteValue, Binary}; |
||||
|
parse_int(Binary) -> |
||||
|
parse_int0(Binary, 0). |
||||
|
|
||||
|
parse_int0(<<Digit, Rest/binary>>, Acc) when Digit >= $0 andalso Digit =< $9 -> |
||||
|
parse_int0(Rest, Acc*10 + Digit - $0); |
||||
|
parse_int0(Binary, Acc) when is_binary(Binary) -> {Acc, Binary}. |
||||
|
|
||||
|
chop_segments([], Acc) -> Acc; |
||||
|
chop_segments([{{X, Y1}, {X, Y2}} | Rest], Acc) -> chop_segments(Rest, [{X, min(Y1, Y2), max(Y1, Y2)} | Acc]); |
||||
|
chop_segments([{{X1, Y}, {X2, Y}} | Rest], Acc) -> chop_segments(Rest, [{X, Y, Y} || X <- lists:seq(min(X1, X2), max(X1, X2))] ++ Acc). |
||||
|
|
||||
|
simulate(State, SandCount) -> |
||||
|
case attempt_place(State, {500, 0}) of |
||||
|
{500, 0} -> SandCount + 1; |
||||
|
{X, Y} -> simulate(State#{X := rset_append({Y, Y}, maps:get(X, State))}, SandCount+1); |
||||
|
falling -> SandCount |
||||
|
end. |
||||
|
|
||||
|
% returns true if there is a block in that spot |
||||
|
test_spot(State, X, Y) -> |
||||
|
case State of |
||||
|
#{X := Column} -> rset_contains(Y, Column); |
||||
|
_ -> false |
||||
|
end. |
||||
|
|
||||
|
attempt_place(State, {X, Y}) -> |
||||
|
Column = maps:get(X, State, []), |
||||
|
case fast_forward(Y, Column) of |
||||
|
[{Floor, _End} | _Rest] -> |
||||
|
case test_spot(State, X-1, Floor) of |
||||
|
false -> attempt_place(State, {X-1, Floor}); % can optimize by shrinking active state here |
||||
|
true -> |
||||
|
case test_spot(State, X+1, Floor) of |
||||
|
false -> attempt_place(State, {X+1, Floor}); % can optimize by shrinking active state here |
||||
|
true -> {X, Floor-1} |
||||
|
end |
||||
|
end; |
||||
|
[] -> falling |
||||
|
end. |
||||
|
|
||||
|
fast_forward(_Any, []) -> []; |
||||
|
fast_forward(Item, [{_,End}|Rest]) when Item > End -> fast_forward(Item, Rest); |
||||
|
fast_forward(_Item, List) -> List. |
||||
|
|
||||
|
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_contains(_Item, []) -> false; |
||||
|
rset_contains(Item, [{Start, _}|_Rest]) when Item < Start -> false; |
||||
|
rset_contains(Item, [{_, End}|Rest]) when Item > End -> rset_contains(Item, Rest); |
||||
|
rset_contains(_Item, _Rest) -> true. |
||||
@ -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]. |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue