Current section

Files

Jump to
beam_olympics src tasks bo_missing_operations.erl
Raw

src/tasks/bo_missing_operations.erl

-module(bo_missing_operations).
-behaviour(bo_task).
-export([description/0, spec/0, score/0, timeout/0, tests/0]).
%%==============================================================================
%% API
%%==============================================================================
-spec description() -> binary().
description() -> <<"Missing operations: You are tasked with finding the correct"
" combination of operators to solve a simple problem. Create"
" a function that receives a list of integers and a solution"
" (another integer). Return a list of operators (the '+' and"
" '-' atoms) that will solve the equation.\n"
"For example, given ([2, 3, 1], 4) return ['+', '-'] because"
" 2 + 3 - 1 = 4. If there's no valid list of operators, just"
" return the atom 'notfound'.">>.
-spec spec() -> bo_task:spec().
spec() ->
#{ input => [<<"[pos_integer()]">>, <<"pos_integer()">>]
, output => <<"notfound | ['+', '-']">>
}.
-spec score() -> 250.
score() -> 250.
-spec timeout() -> 1000.
timeout() -> 1000.
-spec tests() -> [bo_task:test()].
tests() -> [build_test(Case) || Case <- cases()].
build_test({Operands, Solution}) ->
fun(Fun) ->
try Fun(Operands, Solution) of
notfound -> check_impossible(Operands, Solution);
Answer -> check_answer(Operands, Solution, Answer)
catch
_:Error ->
{error, #{ input => [Operands, Solution]
, output => Error
, expected => <<"Not an error, that's for sure.">>}
}
end
end.
%%==============================================================================
%% Utils
%%==============================================================================
cases() ->
[make_case(N) || N <- lists:seq(1, 8)].
make_case(Index) ->
L = [rand:uniform(256) || _ <- lists:seq(1, 2 * Index)],
case rand:uniform() < 0.5 of
true ->
Operators = [case rand:uniform() < 0.5 of
true -> '+';
false -> '-'
end || _ <- lists:seq(1, length(L) - 1)],
{L, test_result(['+' | Operators], L, 0)};
false ->
{L, rand:uniform(256)}
end.
check_answer(Operands, Solution, Answer) ->
case test_result(['+' | Answer], Operands, 0) of
bad_list ->
{error, #{ input => [Operands, Solution]
, output => Answer
, expected => <<"A list of the right length.">>
}};
bad_operator ->
{error, #{ input => [Operands, Solution]
, output => Answer
, expected => <<"A list with the right operators.">>
}};
Solution ->
ok;
Result ->
{error, #{ input => [Operands, Solution]
, output => Answer
, expected => { <<"A list of operators that result in">>
, Solution
, <<"not in">>
, Result
}
}}
end.
test_result(['+' | Operators], [H | T], Acc) ->
test_result(Operators, T, Acc + H);
test_result(['-' | Operators], [H | T], Acc) ->
test_result(Operators, T, Acc - H);
test_result([_BadOperator | _Operations], _Operands, _Acc) ->
bad_operator;
test_result([], [], Acc) ->
Acc;
test_result([], _Operands, _Acc) ->
bad_list;
test_result(_Operators, [], _Acc) ->
bad_list.
check_impossible(Operands, Solution) ->
Combinations = get_operator_combinations(length(Operands) - 1),
case lists:filter(fun(C) ->
Solution =:= test_result(['+' | C], Operands, 0)
end, Combinations) of
[] -> ok;
[_ | _] -> {error, #{ input => [Operands, Solution]
, output => notfound
, expected => <<"Come on, there's a solution.">>
}}
end.
get_operator_combinations(1) ->
[['+'], ['_']];
get_operator_combinations(N) ->
C = get_operator_combinations(N - 1),
[['+' | O] || O <- C] ++ [['-' | O] || O <- C].