4 Commits

  1. 17
      day13.erl
  2. 104
      day14.erl
  3. 62
      day15.erl

17
day13.erl

@ -2,12 +2,10 @@
-export([solve/1]). -export([solve/1]).
-compile(export_all).
solve(Input) -> solve(Input) ->
PacketPairs = binary:split(Input, <<"\n\n">>, [global, trim_all]), PacketPairs = binary:split(Input, <<"\n\n">>, [global, trim_all]),
Part1 = part1(PacketPairs), Part1 = part1(PacketPairs),
{Part1, none}.
{Part1, part2(Input)}.
part1(PacketPairs) -> part1(PacketPairs, {1, 0}). part1(PacketPairs) -> part1(PacketPairs, {1, 0}).
@ -22,6 +20,19 @@ part1([PacketPair|Rest], {Index, Score}) ->
end, end,
part1(Rest, {Index+1, NewScore}). part1(Rest, {Index+1, NewScore}).
part2(Input) ->
Packets0 = binary:split(Input, <<"\n">>, [global, trim_all]),
Packets1 = lists:filter(fun(X) -> byte_size(X) > 0 end, Packets0),
Packets2 = lists:map(fun parse_packet/1, Packets1),
Packets3 = [[[2]], [[6]] | Packets2], % add signal packets
Packets = lists:sort(fun compare/2, Packets3),
find([[2]], Packets) * find([[6]], Packets).
find(Item, List) -> find(Item, List, 1).
find(_Item, [], _Index) -> 0;
find(Item, [Item | _Rest], Index) -> Index;
find(Item, [_Head | Rest], Index) -> find(Item, Rest, Index+1).
compare([], []) -> undecided; compare([], []) -> undecided;
compare([], [_|_Rest]) -> true; compare([], [_|_Rest]) -> true;
compare([_|_Rest], []) -> false; compare([_|_Rest], []) -> false;

104
day14.erl

@ -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.

62
day15.erl

@ -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].
Loading…
Cancel
Save