234 lines
6.0 KiB
Erlang
234 lines
6.0 KiB
Erlang
|
|
-module(day_4).
|
||
|
|
-export([part_1/0, part_2/0]).
|
||
|
|
-export([map_from_matrix/1, mget/3, check_if_cross/3, read_input/1]).
|
||
|
|
|
||
|
|
|
||
|
|
part_1() ->
|
||
|
|
Filename = filename:absname("./priv/input/4.txt"),
|
||
|
|
part_1(Filename).
|
||
|
|
|
||
|
|
part_1(Filename) ->
|
||
|
|
Input = read_input(Filename),
|
||
|
|
|
||
|
|
Xmases = [
|
||
|
|
find_xmas_in_matrix(Input),
|
||
|
|
find_xmas_in_matrix(transpose(Input)),
|
||
|
|
find_xmas_in_matrix(transpose(skew(Input))),
|
||
|
|
find_xmas_in_matrix(transpose(skew(Input, reversed)))
|
||
|
|
],
|
||
|
|
|
||
|
|
lists:sum(Xmases).
|
||
|
|
|
||
|
|
|
||
|
|
part_2() ->
|
||
|
|
Filename = filename:absname("./priv/input/4.txt"),
|
||
|
|
part_2(Filename).
|
||
|
|
|
||
|
|
part_2(Filename) ->
|
||
|
|
Input = read_input(Filename),
|
||
|
|
Matrix = map_from_matrix(Input),
|
||
|
|
Coords = maps:fold(
|
||
|
|
fun(Y, Line, Acc) ->
|
||
|
|
As = maps:fold(
|
||
|
|
fun(X, C, AccLine) ->
|
||
|
|
case C of
|
||
|
|
$A -> [{X, Y} | AccLine];
|
||
|
|
_ -> AccLine
|
||
|
|
end
|
||
|
|
end, [], Line),
|
||
|
|
[As | Acc]
|
||
|
|
end,
|
||
|
|
[],
|
||
|
|
Matrix),
|
||
|
|
ACoords = lists:append(Coords),
|
||
|
|
Xmases = lists:filter(fun({X,Y}) -> check_if_cross(X, Y, Matrix) end, ACoords),
|
||
|
|
length(Xmases).
|
||
|
|
|
||
|
|
|
||
|
|
%% Private functions
|
||
|
|
|
||
|
|
read_input(Filename) ->
|
||
|
|
{ok, Filehandle} = file:open(Filename, read),
|
||
|
|
read_input(Filehandle, []).
|
||
|
|
|
||
|
|
read_input(Filehandle, Lines) ->
|
||
|
|
case file:read_line(Filehandle) of
|
||
|
|
{ok, Line} ->
|
||
|
|
Trimmed = string:trim(Line),
|
||
|
|
read_input(Filehandle, [Trimmed | Lines]);
|
||
|
|
eof ->
|
||
|
|
lists:reverse(Lines)
|
||
|
|
end.
|
||
|
|
|
||
|
|
find_xmas_in_matrix(Matrix) ->
|
||
|
|
lists:foldl(
|
||
|
|
fun(Line, Sum) ->
|
||
|
|
Xmas = find_xmas_in_line(list_to_binary(Line)),
|
||
|
|
Samx = find_xmas_in_line(list_to_binary(string:reverse(Line))),
|
||
|
|
Sum + Xmas + Samx
|
||
|
|
end,
|
||
|
|
0,
|
||
|
|
Matrix
|
||
|
|
).
|
||
|
|
|
||
|
|
find_xmas_in_line(<<"XMAS", Rest/binary>>) ->
|
||
|
|
1 + find_xmas_in_line(<<Rest/binary>>);
|
||
|
|
find_xmas_in_line(<<_,Rest/binary>>) ->
|
||
|
|
find_xmas_in_line(Rest);
|
||
|
|
find_xmas_in_line(<<>>) ->
|
||
|
|
0.
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
transpose([[]|_]) -> [];
|
||
|
|
transpose(Matrix) ->
|
||
|
|
[lists:map(fun hd/1, Matrix) | transpose(lists:map(fun tl/1, Matrix))].
|
||
|
|
|
||
|
|
%% rotate([[]|_]) -> [];
|
||
|
|
%% rotate(Matrix) ->
|
||
|
|
%% lists:map(fun lists:reverse/1, transpose(Matrix)).
|
||
|
|
|
||
|
|
skew(Matrix) ->
|
||
|
|
Length = length(Matrix),
|
||
|
|
{Skewed, _} =
|
||
|
|
lists:mapfoldl(
|
||
|
|
fun(Line, Index) ->
|
||
|
|
%% {Init, Tail} = lists:split(Index, Line),
|
||
|
|
%% {lists:append([Tail, Init]) , Index + 1}
|
||
|
|
{
|
||
|
|
lists:append(
|
||
|
|
[
|
||
|
|
lists:duplicate(Index, " "),
|
||
|
|
Line,
|
||
|
|
lists:duplicate(Length - Index - 1, " ")
|
||
|
|
]
|
||
|
|
),
|
||
|
|
Index + 1
|
||
|
|
}
|
||
|
|
end,
|
||
|
|
0,
|
||
|
|
Matrix),
|
||
|
|
Skewed.
|
||
|
|
|
||
|
|
skew(Matrix, reversed) ->
|
||
|
|
Length = length(Matrix),
|
||
|
|
{Skewed, _} =
|
||
|
|
lists:mapfoldl(
|
||
|
|
fun(Line, Index) ->
|
||
|
|
%% {Init, Tail} = lists:split(Length - Index, Line),
|
||
|
|
%% {lists:append([Tail, Init]) , Index + 1}
|
||
|
|
{
|
||
|
|
lists:append(
|
||
|
|
[
|
||
|
|
lists:duplicate(Length - Index - 1, " "),
|
||
|
|
Line,
|
||
|
|
lists:duplicate(Index, " ")
|
||
|
|
]
|
||
|
|
),
|
||
|
|
Index + 1
|
||
|
|
}
|
||
|
|
end,
|
||
|
|
0,
|
||
|
|
Matrix),
|
||
|
|
Skewed.
|
||
|
|
|
||
|
|
%% Part 2
|
||
|
|
|
||
|
|
map_from_matrix(Matrix) ->
|
||
|
|
{List, _Length} = lists:mapfoldl(
|
||
|
|
fun(Line, Index) -> {{Index, map_from_line(Line)}, Index + 1} end,
|
||
|
|
0,
|
||
|
|
Matrix),
|
||
|
|
maps:from_list(List).
|
||
|
|
|
||
|
|
map_from_line(Line) ->
|
||
|
|
{List, _Length} = lists:mapfoldl(
|
||
|
|
fun(Char, Index) -> {{Index, Char}, Index + 1} end,
|
||
|
|
0,
|
||
|
|
Line),
|
||
|
|
maps:from_list(List).
|
||
|
|
|
||
|
|
mget(Matrix, X, Y) ->
|
||
|
|
maps:get(X, maps:get(Y, Matrix)).
|
||
|
|
|
||
|
|
check_if_cross(0, _, _Matrix) ->
|
||
|
|
false;
|
||
|
|
check_if_cross(_, 0, _Matrix) ->
|
||
|
|
false;
|
||
|
|
check_if_cross(X, Y, Matrix) ->
|
||
|
|
maybe
|
||
|
|
true ?= maps:size(Matrix) - 1 > Y,
|
||
|
|
Line = maps:get(Y,Matrix),
|
||
|
|
true ?= maps:size(Line) - 1 > X,
|
||
|
|
"MS" ?= lists:sort([
|
||
|
|
mget(Matrix, X - 1, Y - 1),
|
||
|
|
mget(Matrix, X + 1, Y + 1)
|
||
|
|
]),
|
||
|
|
"MS" ?= lists:sort([
|
||
|
|
mget(Matrix, X - 1, Y + 1),
|
||
|
|
mget(Matrix, X + 1, Y - 1)
|
||
|
|
]),
|
||
|
|
true
|
||
|
|
else
|
||
|
|
_ -> false
|
||
|
|
end.
|
||
|
|
|
||
|
|
%% TESTS
|
||
|
|
|
||
|
|
-ifdef(TEST).
|
||
|
|
-include_lib("eunit/include/eunit.hrl").
|
||
|
|
|
||
|
|
find_xmas_in_line_test() ->
|
||
|
|
?assertEqual(0, find_xmas_in_line(<<>>)),
|
||
|
|
?assertEqual(0, find_xmas_in_line(<<"ABC">>)),
|
||
|
|
?assertEqual(1, find_xmas_in_line(<<"XMASAAAAA">>)),
|
||
|
|
?assertEqual(1, find_xmas_in_line(<<"XXMASS">>)),
|
||
|
|
?assertEqual(1, find_xmas_in_line(<<"SAMXMAS">>)),
|
||
|
|
?assertEqual(1, find_xmas_in_line(<<"XMASAMX">>)).
|
||
|
|
|
||
|
|
|
||
|
|
transpose_test() ->
|
||
|
|
?assertEqual(
|
||
|
|
["100",
|
||
|
|
"010",
|
||
|
|
"001"
|
||
|
|
],
|
||
|
|
transpose(["100",
|
||
|
|
"010",
|
||
|
|
"001"
|
||
|
|
])).
|
||
|
|
|
||
|
|
skew_test() ->
|
||
|
|
?assertEqual(
|
||
|
|
lists:map(fun list_to_binary/1,
|
||
|
|
["100 ",
|
||
|
|
" 100 ",
|
||
|
|
" 100"
|
||
|
|
]),
|
||
|
|
lists:map(fun list_to_binary/1, skew(
|
||
|
|
["100",
|
||
|
|
"100",
|
||
|
|
"100"
|
||
|
|
]))),
|
||
|
|
?assertEqual(
|
||
|
|
lists:map(fun list_to_binary/1,
|
||
|
|
[" 100",
|
||
|
|
" 100 ",
|
||
|
|
"100 "
|
||
|
|
]),
|
||
|
|
lists:map(fun list_to_binary/1, skew(
|
||
|
|
["100",
|
||
|
|
"100",
|
||
|
|
"100"
|
||
|
|
], reversed))).
|
||
|
|
|
||
|
|
part_1_test() ->
|
||
|
|
Testfile = filename:absname("./priv/test_input/4.txt"),
|
||
|
|
?assertEqual(18, part_1(Testfile)).
|
||
|
|
|
||
|
|
part_2_test() ->
|
||
|
|
Testfile = filename:absname("./priv/test_input/4.txt"),
|
||
|
|
?assertEqual(9, part_2(Testfile)).
|
||
|
|
|
||
|
|
-endif.
|