-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(<>); 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.