From 2ff41f3c32a089dd73cc92b15384fcb0df1a2b6f Mon Sep 17 00:00:00 2001 From: Shanti Chellaram Date: Mon, 19 Dec 2022 16:17:41 +0900 Subject: [PATCH] Day 14-1 --- day14.erl | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 day14.erl diff --git a/day14.erl b/day14.erl new file mode 100644 index 0000000..9b39f4a --- /dev/null +++ b/day14.erl @@ -0,0 +1,97 @@ +-module(day14). + +-export([solve/1]). + +solve(Input) -> + {part1(Input), none}. + + +part1(Input) -> simulate(parse_map(Input), 0). + +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(<>, 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 + {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. + +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].