This topic is about Day 22 of the Advent of Code 2021.
We have a private leaderboard (shared with users of the elixir forum):
https://adventofcode.com/2021/leaderboard/private/view/370884
The entry code is:
370884-a6a71927
This topic is about Day 22 of the Advent of Code 2021.
We have a private leaderboard (shared with users of the elixir forum):
https://adventofcode.com/2021/leaderboard/private/view/370884
The entry code is:
370884-a6a71927
My solution for day 22; only part1 really works; having a look at the elixer forum there seems to be some range function used by all solutions there at least for part2.
I am not sure if Diagraph could be used for part 2 or if some way of recording of intersections could be used for providing a solution to part2. Or possibly just some mathematical function could be used; would be keen if anyone tries it to see how it could be done.
-module(day_22).
-compile([export_all]).
-record(inst, {set,
x_range = {from,to},
y_range = {from,to},
z_range = {from,to}
}).
p1() ->
Inst = input(),
ValidInst = validate_inst(Inst),
%% {Inst, ValidInst}.
MapResult = make_map(ValidInst, #{}),
lists:sum([1|| {_,X} <- maps:to_list(MapResult),
X == on
]).
p2() ->
Inst = input(),
MapResult = make_map2(Inst, #{}),
lists:sum([1|| {_,X} <- maps:to_list(MapResult),
X == on
]).
make_map([], Map) ->
Map;
make_map([#inst{set = Set,
x_range = {XF, XT},
y_range = {YF, YT},
z_range = {ZF, ZT}}|TailInst],
Map) ->
Coords = [{X, Y, Z} || X <- lists:seq(XF, XT),
Y <- lists:seq(YF,YT),
Z <- lists:seq(ZF,ZT),
X >= -50, X =< 50,
Y >= -50, Y =< 50,
Z >= -50, Z =< 50],
NewMap = set_coords(Coords, Set, Map),
io:format("1~n", []),
make_map(TailInst, NewMap).
make_map2([], Map) ->
Map;
make_map2([#inst{set = Set,
x_range = {XF, XT},
y_range = {YF, YT},
z_range = {ZF, ZT}}|TailInst],
Map) ->
Coords = [{X, Y, Z} || X <- lists:seq(XF, XT),
Y <- lists:seq(YF,YT),
Z <- lists:seq(ZF,ZT),
X >= -50, X =< 50,
Y >= -50, Y =< 50,
Z >= -50, Z =< 50],
NewMap = set_coords(Coords, Set, Map),
io:format("1~n", []),
make_map2(TailInst, NewMap).
set_coords([], _, Map) ->
Map;
set_coords([E|Tail],Set, Map) ->
set_coords(Tail, Set,
maps:put(E,Set, Map)
).
validate_inst([]) ->
[];
validate_inst([I = #inst{
x_range = {XF, XT},
y_range = {YF, YT},
z_range = {ZF, ZT}
}
| Tail]) ->
%% Validate Range resonable.
%%
case {valid(XF, XT), valid(YF, YT), valid(ZF, ZT)} of
{valid, valid, valid} ->
[I|validate_inst(Tail)];
_ ->
validate_inst(Tail)
end.
valid(A, B) when A > 50 , B > 50 ;
A < -50, B < -50 ->
invalid;
valid(_,_) ->
valid.
%% Parse Input
input() ->
{ok, Bin} = file:read_file("priv/input.txt"),
Lines = string:tokens(binary_to_list(Bin), "\n"),
parse_instructions(Lines).
parse_instructions([]) -> [];
parse_instructions([E|Tail]) ->
{Inst, Rest} = case string:tokens(E, " ") of
["on",Rest] ->
{on,Rest};
["off",Rest] ->
{off, Rest}
end,
[XF, XT, YF, YT, ZF, ZT] = string:tokens(Rest, "x=yz.,"),
[#inst{set = Inst,
x_range = {list_to_integer(XF), list_to_integer(XT)},
y_range = {list_to_integer(YF), list_to_integer(YT)},
z_range = {list_to_integer(ZF), list_to_integer(ZT)}
}|
parse_instructions(Tail)].
The Elixir Range
type can easily be implemented in Erlang using a tuple: {From, To}
. Thus, the range 1..10
would be represented as the tuple {1, 10}
. To get the size of a range implemented in that way, use:
range_size({From, To}) ->
max(To - From + 1, 0).
UPDATED: To test for disjoint ranges, use:
range_are_disjoint({From1, To1}, {From2, To2}) ->
To2 < From1 orelse To1 < From2.
a rare (for me) occasion to put a solution into one screen of code
bjorng, thanks for present this AoC. These challenges are an interesting option to try what I already learned and learn from the experts.
I didn’t expected get the code running slower when using sets instead of ordsets.
But, I got the right answer - for the part one of the challenge. For the second one I got a huge memory crash.
-module(aoc2021_d22).
-export([main/0]).
-define(LO_RANGE, -50).
-define(HI_RANGE, +50).
main() ->
Data = get_data(),
CubesOn = reboot_proc(Data),
io:format("Cubes turned On: ~p\n",[ordsets:size(CubesOn)]).
get_data() ->
case file:read_file("aoc2021_d22-input.txt") of
{ok, File} ->
Buff = string:split(unicode:characters_to_list(File),"\n",all),
parse_data(Buff,[]);
_ -> []
end.
reboot_proc(Data) ->
reboot_proc(Data, ordsets:new()).
reboot_proc([], Acc) -> Acc;
reboot_proc([Row|Rows], Acc0) ->
Acc = reboot_step(Row, Acc0),
reboot_proc(Rows, Acc).
reboot_step({_, []}, Acc) -> Acc;
reboot_step({on, [X,Y,Z]}, Acc0) ->
Acc = calc_cuboid(X,Y,Z,[]),
Acc1 = ordsets:from_list(Acc),
ordsets:union(Acc0,Acc1);
reboot_step({off, [X,Y,Z]}, Acc0) ->
Acc = calc_cuboid(X,Y,Z,[]),
Acc1 = ordsets:from_list(Acc),
ordsets:subtract(Acc0,Acc1).
calc_cuboid({X0,X1}, _, _, Acc) when X0 > X1 -> Acc;
calc_cuboid({X0,X1}=X, Y, Z, Acc0) ->
Acc = calc_cuboid_y(X,Y,Z,Acc0),
calc_cuboid({X0+1,X1},Y,Z,Acc).
calc_cuboid_y(_, {Y0,Y1}, _, Acc) when Y0 > Y1 -> Acc;
calc_cuboid_y(X, {Y0,Y1}=Y, Z, Acc0) ->
Acc = calc_cuboid_z(X,Y,Z, Acc0),
calc_cuboid_y(X,{Y0+1,Y1},Z,Acc).
calc_cuboid_z(_, _, {Z0,Z1}, Acc) when Z0 > Z1 -> Acc;
calc_cuboid_z({X0,_}=X, {Y0,_}=Y, {Z0,Z1}, Acc0) ->
calc_cuboid_z(X,Y,{Z0+1,Z1},[{X0,Y0,Z0}|Acc0]).
validate_range({Lo,Hi}) ->
if ((Lo < ?LO_RANGE) and (Hi < ?LO_RANGE)) or
((Lo > ?HI_RANGE) and (Hi > ?HI_RANGE)) ->
false;
true ->
{min(max(Lo,?LO_RANGE),?HI_RANGE),
max(min(Hi,?HI_RANGE),?LO_RANGE)}
end.
parse_data([], Acc) ->
lists:reverse(Acc);
parse_data([[]], Acc) ->
parse_data([], Acc);
parse_data([Data|Buff], Acc) ->
parse_data(Buff, [parse_row(Data)|Acc]).
parse_row(Row) ->
[Cmd,Dim] = string:split(Row," "),
{list_to_atom(Cmd),parse_values(Dim)}.
parse_values(Dim) ->
DimValues = string:split(Dim,",", all),
parse_value(DimValues,[]).
parse_value([], Acc) -> Acc;
parse_value([DimValue|DimValues], Acc) ->
[_Coord,Range] = string:split(DimValue,"="),
case string:split(Range,"..") of
[Lo0,Hi0] ->
case validate_range({list_to_integer(Lo0),list_to_integer(Hi0)}) of
{Lo,Hi} -> parse_value(DimValues,[{Lo,Hi}|Acc]);
_ -> []
end;
_ -> []
end.
You will get better performance for sets
if you use the {version, 2}
option when creating the sets:
Acc1 = sets:from_list(Acc, [{version, 2}]),
Here is the documentation for {version, 2}
:
Erlang/OTP 24.0 introduced a new internal representation for sets which is more performant. Developers can use this new representation by passing the
{version, 2}
flag to new/1 and from_list/2, such assets:new([{version, 2}])
. This new representation will become the default in future Erlang/OTP versions.