diff --git a/src/aoc2024.app.src b/src/aoc2024.app.src index f1336fa..2e73a6b 100644 --- a/src/aoc2024.app.src +++ b/src/aoc2024.app.src @@ -7,7 +7,10 @@ stdlib ]}, {env, []}, - {modules, []}, + {modules, [ + day_1, + day_2 + ]}, {licenses, ["Apache-2.0"]}, {links, []} ]}. diff --git a/src/day_2.erl b/src/day_2.erl new file mode 100644 index 0000000..f13e61b --- /dev/null +++ b/src/day_2.erl @@ -0,0 +1,125 @@ +-module(day_2). + +-export([part_1/0, part_2/0]). + +part_1() -> + Filename = open_file(), + part_1(Filename). + +part_1(Filename) -> + Reports = parse_input(Filename), + SafeReports = lists:filter(fun is_safe/1, Reports), + length(SafeReports). + +part_2() -> + Filename = open_file(), + part_2(Filename). + +part_2(Filename) -> + Reports = parse_input(Filename), + SafeReports = lists:filter(fun is_tolerably_safe/1, Reports), + length(SafeReports). + +%% Private Functions + +open_file() -> + Filename = filename:absname("./priv/input/2.txt"), + open_file(Filename). + +open_file(Filename) -> + {ok, File} = file:open(Filename, read), + File. + +parse_input(Filename) -> + parse_input(Filename, []). + +parse_input(Filename, Reports) -> + case file:read_line(Filename) of + {ok, Line} -> + Trimmed = string:trim(Line), + Chunks = string:split(Trimmed, " ", all), + NonEmpty = lists:filter(fun(L) -> length(L) > 0 end, Chunks), + Report = lists:map(fun list_to_integer/1, NonEmpty), + parse_input(Filename, [Report | Reports]); + eof -> + Reports + end. + +check_report([_Whatever | []]) -> + safe; +check_report([A | [B | _] = Rest]) when A < B andalso B - A < 4 -> + check_report(Rest, asc); +check_report([A | [B | _] = Rest]) when A > B andalso A - B < 4 -> + check_report(Rest, desc); +check_report(Rest) -> + unsafe. + +check_report([_A], _Whatever) -> + safe; +check_report([A, B], asc) when A < B andalso B - A < 4 -> + safe; +check_report([A, B], desc) when A > B andalso A - B < 4 -> + safe; +check_report([A | [B | _] = Rest], asc) when A < B andalso B - A < 4 -> + check_report(Rest, asc); +check_report([A | [B | _] = Rest], desc) when A > B andalso A - B < 4 -> + check_report(Rest, desc); +check_report(_Rest, _Direction) -> + unsafe. + +is_safe(Report) -> + safe == check_report(Report). + +check_report_with_tolerance(Report) -> + case check_report(Report) of + safe -> + safe; + unsafe -> + check_report_by_removing_levels(Report) + end. + +check_report_by_removing_levels([H | Tail]) -> + case check_report(Tail) of + safe -> safe; + unsafe -> + check_report_by_removing_levels([H], Tail) + end. + +check_report_by_removing_levels(_Init, []) -> + unsafe; +check_report_by_removing_levels(Init, [H | Tail]) -> + case check_report(Init ++ Tail) of + safe -> + safe; + unsafe -> + check_report_by_removing_levels(Init ++ [H], Tail) + end. + + +is_tolerably_safe(Report) -> + safe == check_report_with_tolerance(Report). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +part_1_test() -> + TestFile = test_data(), + ?assertEqual(2, part_1(TestFile)). + +part_2_test() -> + TestFile = test_data(), + ?assertEqual(4, part_2(TestFile)). + +is_safe_4_test() -> ?assertEqual(safe, check_report([7, 6, 4, 2, 1])). +is_safe_5_test() -> ?assertEqual(unsafe, check_report([1, 2, 7, 8, 9])). +is_safe_6_test() -> ?assertEqual(unsafe, check_report([9, 7, 6, 2, 1])). +is_safe_7_test() -> ?assertEqual(unsafe, check_report([1, 3, 2, 4, 5])). +is_safe_8_test() -> ?assertEqual(unsafe, check_report([8, 6, 4, 4, 1])). +is_safe_9_test() -> ?assertEqual(safe, check_report([1, 3, 6, 7, 9])). +is_safe_10_test() -> ?assertEqual(safe, check_report([40, 42, 44, 46, 49, 51])). + + +test_data() -> + open_file("./priv/test_input/2.txt"). + +-endif.