Skip to content

Instantly share code, notes, and snippets.

@joeytrapp
Created December 5, 2024 04:04
Show Gist options
  • Save joeytrapp/dccd46391e4fb6f2f06b39b92de43f30 to your computer and use it in GitHub Desktop.
Save joeytrapp/dccd46391e4fb6f2f06b39b92de43f30 to your computer and use it in GitHub Desktop.
Advent of Code Day 4 Part 1 and Part 2
-module(aoc).
-export([main/1]).
main([Path]) ->
{ok, Content} = file:read_file(Path),
Data = fill(Content),
YSize = array:size(Data),
XSize = array:size(array:get(0, Data)),
persistent_term:put(?MODULE, Data),
Strings = make_strings(XSize, YSize),
io:format("Xmas Sum: ~p~n", [lists:sum(lists:map(fun test_binary/1, Strings))]),
io:format("Cross Sum: ~p~n", [lists:sum(find_x_mas(XSize, YSize))]),
erlang:halt(0).
fill(Binary) when is_binary(Binary) ->
Lines = binary:split(Binary, <<"\n">>, [global, trim_all]),
fill(Lines, array:new(length(Lines))).
fill(Lines, Arr) ->
Struct = lists:foldl(fun(Bin, {I, AccIn}) ->
AccOut = array:set(I, fill_row(Bin, array:new(string:length(Bin)), 0), AccIn),
{I+1, AccOut}
end, {0, Arr}, Lines),
element(2, Struct).
fill_row(<<>>, Arr, _) -> Arr;
fill_row(<<C:1/binary, T/binary>>, Arr, Iter) -> fill_row(T, array:set(Iter, C, Arr), Iter+1).
make_strings(XSize, YSize) ->
Bounds = {{0,0},{XSize-1,YSize-1}},
L1 = range(0, YSize, fun(I) -> make_line(fun horizontal/2, {0,I}, Bounds) end),
L2 = range(0, XSize, fun(I) -> make_line(fun vertical/2, {I,0}, Bounds) end),
L3 = range(0, YSize, fun(I) -> make_line(fun diagonal_down/2, {0,I}, Bounds) end),
L4 = range(1, XSize, fun(I) -> make_line(fun diagonal_down/2, {I,0}, Bounds) end),
L5 = range(0, YSize, fun(I) -> make_line(fun diagonal_up/2, {0,I}, Bounds) end),
L6 = range(1, XSize, fun(I) -> make_line(fun diagonal_up/2, {I,YSize-1}, Bounds) end),
lists:append(L1, lists:append(L2, lists:append(L3, lists:append(L4, lists:append(L5, L6))))).
find_x_mas(XSize, YSize) ->
L = range(1, YSize-1, fun(Y) ->
range(1, XSize-1, fun(X) ->
test_x_mas(lookup({X,Y}), X, Y)
end)
end),
lists:flatten(L).
test_x_mas(<<"A">>, X, Y) ->
case {lookup({X-1,Y-1}), lookup({X+1,Y-1}), lookup({X-1,Y+1}), lookup({X+1,Y+1})} of
{<<"M">>, <<"M">>, <<"S">>, <<"S">>} -> 1;
{<<"S">>, <<"S">>, <<"M">>, <<"M">>} -> 1;
{<<"M">>, <<"S">>, <<"M">>, <<"S">>} -> 1;
{<<"S">>, <<"M">>, <<"S">>, <<"M">>} -> 1;
_ -> 0
end;
test_x_mas(_, _, _) -> 0.
lookup({X,Y}) when is_integer(X), is_integer(Y) -> lookup({X,Y}, persistent_term:get(?MODULE)).
lookup({X,Y}, Arr) when is_integer(X), is_integer(Y) ->
array:get(X, array:get(Y, Arr)).
test_binary(Bin) -> test_binary(Bin, 0).
test_binary(<<>>, Acc) -> Acc;
test_binary(<<"XMAS", _/binary>> = <<_:1/binary, T/binary>>, Acc) -> test_binary(T, Acc + 1);
test_binary(<<"SAMX", _/binary>> = <<_:1/binary, T/binary>>, Acc) -> test_binary(T, Acc + 1);
test_binary(<<_:1/binary, T/binary>>, Acc) -> test_binary(T, Acc).
range(A, B, Func) -> range(A, B, Func, []).
range(A, B, _, Acc) when A == B -> lists:reverse(Acc);
range(A, B, Func, Acc) when A < B -> range(A+1, B, Func, [Func(A) | Acc]);
range(A, B, Func, Acc) when A > B -> range(A-1, B, Func, [Func(A) | Acc]).
make_line(Transform, Point, Bounds) ->
make_line(Transform, Point, Bounds, 0, <<>>).
make_line(Transform, Point, Bounds, I, Bin) ->
Char = lookup(Point),
Updated = <<Bin/binary, Char/binary>>,
case Transform(Point, Bounds) of
oob -> Updated;
NextPoint -> make_line(Transform, NextPoint, Bounds, I+1, Updated)
end.
vertical({X,Y}, {{_MinX,_MinY},{_MaxX,MaxY}}) ->
case Y+1 > MaxY of
true -> oob;
false -> {X, Y+1}
end.
horizontal({X,Y}, {{_MinX,_MinY},{MaxX,_MaxY}}) ->
case X+1 > MaxX of
true -> oob;
false -> {X+1, Y}
end.
diagonal_down({X,Y}, {{_MinX,_MinY},{MaxX,MaxY}}) ->
case {X+1 > MaxX, Y+1 > MaxY} of
{true, _} -> oob;
{_, true} -> oob;
_ -> {X+1,Y+1}
end.
diagonal_up({X,Y}, {{_MinX,MinY},{MaxX,_MaxY}}) ->
case {X+1 > MaxX, Y-1 < MinY} of
{true, _} -> oob;
{_, true} -> oob;
_ -> {X+1,Y-1}
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment