Skip to content

Instantly share code, notes, and snippets.

@7-fl
Last active March 12, 2017 21:40
Show Gist options
  • Select an option

  • Save 7-fl/b1081d97196a41186fa0dbfcbebb1006 to your computer and use it in GitHub Desktop.

Select an option

Save 7-fl/b1081d97196a41186fa0dbfcbebb1006 to your computer and use it in GitHub Desktop.
Finished Implementing tests for apply_best_strat()
-module(rps).
-compile(export_all).
-include_lib("eunit/include/eunit.hrl").
%
% play one strategy against another, for N moves.
%
play_two(StrategyL,StrategyR,N) ->
play_two(StrategyL,StrategyR,[],[],N).
% tail recursive loop for play_two/3
% 0 case computes the result of the tournament
% FOR YOU TO DEFINE
% REPLACE THE dummy DEFINITIONS
play_two(_,_,PlaysL,PlaysR,0) ->
dummy;
play_two(StrategyL,StrategyR,PlaysL,PlaysR,N) ->
dummy.
%
% interactively play against a strategy, provided as argument.
%
play(Strategy) ->
io:format("Rock - paper - scissors~n"),
io:format("Play one of rock, paper, scissors, ...~n"),
io:format("... r, p, s, stop, followed by '.'~n"),
play(Strategy,[]).
% tail recursive loop for play/1
play(Strategy,Moves) ->
{ok,P} = io:read("Play: "),
Play = expand(P),
case Play of
stop ->
io:format("Stopped~n");
_ ->
Result = result(Play,Strategy(Moves)),
io:format("Result: ~p~n",[Result]),
play(Strategy,[Play|Moves])
end.
%
% auxiliary functions
%
% transform shorthand atoms to expanded form
expand(r) -> rock;
expand(p) -> paper;
expand(s) -> scissors;
expand(X) -> X.
% result of one set of plays
result(rock,rock) -> draw;
result(rock,paper) -> lose;
result(rock,scissors) -> win;
result(paper,rock) -> win;
result(paper,paper) -> draw;
result(paper,scissors) -> lose;
result(scissors,rock) -> lose;
result(scissors,paper) -> win;
result(scissors,scissors) -> draw.
% result of a tournament
tournament(PlaysL,PlaysR) ->
lists:sum(
lists:map(fun outcome/1,
lists:zipwith(fun result/2,PlaysL,PlaysR))).
outcome(win) -> 1;
outcome(lose) -> -1;
outcome(draw) -> 0.
% transform 0, 1, 2 to rock, paper, scissors and vice versa.
enum(0) ->
rock;
enum(1) ->
paper;
enum(2) ->
scissors.
val(rock) ->
0;
val(paper) ->
1;
val(scissors) ->
2.
% give the play which the argument beats.
beats(rock) ->
paper;
beats(paper) ->
scissors;
beats(scissors) ->
rock.
%
% strategies.
%
echo([]) ->
paper;
echo([Last|_]) ->
Last.
rock(_) ->
rock.
% FOR YOU TO DEFINE
% REPLACE THE dummy DEFINITIONS
%---------
no_repeat_test() ->
Play1 = no_repeat([]),
true = w2:member(Play1, plays()),
OpponentPlays = [rock],
Response = no_repeat(OpponentPlays),
[Hd|_] = OpponentPlays,
true = w2:member(Response, others(Hd)),
all_tests_passed.
no_repeat([]) -> %First play and no list of opponent's plays.
%After implementing rand(), I can call that instead of the following:
%% Plays = plays(),
%% Rand = random:uniform(x:len(Plays)),
%% get_elmt(Rand, Plays);
rand([]);
no_repeat([Play|_]) ->
Others = others(Play),
RandNum = random:uniform(x:len(Others)),
get_elmt(RandNum, Others).
plays() ->
[rock, paper, scissors].
others_test() ->
[scissors, paper] = others(rock),
[scissors, rock] = others(paper),
[paper, rock] = others(scissors),
all_tests_passed.
others(Play) ->
others(Play, plays(), []).
others(_Play, [], Others) ->
Others;
others(Play, [Play|Ps], Others)->
others(Play, Ps, Others);
others(Play, [P|Ps], Others) ->
others(Play, Ps, [P|Others]).
%-------------
const_test() ->
Response = rock([rock, paper]),
Response = const([]),
Response = const([paper, rock]),
all_tests_passed.
const(Ps) ->
rock(Ps).
%---------
%% Can't test this:
%%
%% rand(Ps) ->
%% Plays = plays(),
%% Rand = random:uniform(x:len(Plays)),
%% get_elmt(Rand, Plays).
rand_test() ->
Plays = plays(),
NumPlays = x:len(Plays),
RandNum1 = random:uniform(NumPlays),
RandNum2 = random:uniform(NumPlays),
RandNum3 = random:uniform(NumPlays),
CorrectElmt1 = get_elmt(RandNum1, Plays),
CorrectElmt2 = get_elmt(RandNum2, Plays),
CorrectElmt3 = get_elmt(RandNum3, Plays),
RandFunc1 = fun(_X) -> RandNum1 end,
RandFunc2 = fun(_X) -> RandNum2 end,
RandFunc3 = fun(_X) -> RandNum3 end,
CorrectElmt1 = rand([], RandFunc1),
CorrectElmt2 = rand([rock], RandFunc2),
CorrectElmt3 = rand([rock,paper,scissors], RandFunc3),
all_tests_passed.
rand(Plays) ->
rand(Plays, fun random:uniform/1). %I implemented rand/2, so that I could test rand/2.
rand(_Ps, RandFunc) -> %Handles empty list of opponent plays.
Plays = plays(),
RandNum = RandFunc(x:len(Plays)),
get_elmt(RandNum, Plays).
get_elmt_test() ->
a = get_elmt(1, [a, b, c]),
b = get_elmt(2, [a, b, c]),
c = get_elmt(3, [a, b, c]),
all_tests_passed.
get_elmt(1, [P|_]) ->
P;
get_elmt(N, [_P|Ps]) when N>=1 ->
get_elmt(N-1, Ps).
%-----------------
cycle_test() ->
rock = cycle([]),
paper = cycle([rock]),
scissors = cycle([paper]),
rock = cycle([scissors]),
all_tests_passed.
cycle(Plays) -> %Algorithm: return the play after the position of the
cycle(Plays, plays()). %opponent's play in the plays() list.
cycle([], [Play|_Plays]) -> %No opponent plays, response is first play in plays(), i.e. rock.
Play;
cycle([P|_Ps], [P|[]]) -> %Opponent's play matches last one in plays(), response is first play.
[Response|_] = plays(),
Response;
cycle([P|_Ps], [P|Plays]) -> %Response is next play after opponent's play.
[Response|_] = Plays,
Response;
cycle(Ps, [_|Plays] ) ->
cycle(Ps, Plays).
%--------------
least_freq_test() ->
Response1 = beats(paper),
Response1 = least_freq([rock, scissors, paper, rock, scissors]),
Response2 = beats(rock),
Response2 = least_freq([rock, paper, paper, scissors, scissors]),
Response3 = least_freq([scissors]), %=> paper, rock will have freqs of 0
true = w2:member(Response3, [paper, scissors]), %beats [rock, paper] => [paper, scissors]
Response4 = least_freq([]),
true = w2:member(Response4, plays() ),
all_tests_passed.
least_freq([]) ->
%rand([]);
rock;
least_freq(Plays) ->
Freqs = get_freqs(Plays),
MinFunc = fun(X, Y) -> %Compare the simplicity of an if-stmt to the
if %nested case-stmts that do th same thing below.
X<Y -> repl;
X=:=Y -> add;
X>Y -> skip
end
end,
%MinFreqs = best_freqs(Freqs, MinFunc), %There could be more than one...
%RandNum = random:uniform( x:len(MinFreqs) ), %so randomly pick one.
%{Play, _Freq} = get_elmt(RandNum,MinFreqs), %Then return whatever Play beats it.
%io:format("~w~n", [Play]),
[{Play, _Freq}|_] = best_freqs(Freqs, MinFunc), %There could be more than one...
beats(Play).
%--------------
best_freqs_test() ->
%Collect minimum frequencies:
MinFunc = fun(X, Y) ->
case X<Y of
true -> repl;
false -> case X>Y of
true -> skip;
false -> add
end
end
end,
%Collect maximum frequencies:
MaxFunc = fun(X, Y) ->
case X<Y of
true -> skip;
false -> case X>Y of
true -> repl;
false -> add
end
end
end,
[{b,1}] = best_freqs([{a,2},{b,1},{c,3}], MinFunc),
[{c,1}, {b,1}] = best_freqs([{a,2},{b,1},{c,1}], MinFunc),
[{c,1}, {b,1}, {a,1}] = best_freqs([{a,1},{b,1},{c,1}], MinFunc),
[{c,3}] = best_freqs([{a,2},{b,1},{c,3}], MaxFunc),
[{c,2}, {a,2}] = best_freqs([{a,2},{b,1},{c,2}], MaxFunc),
[{c,1}, {b,1}, {a,1}] = best_freqs([{a,1},{b,1},{c,1}], MaxFunc),
all_tests_passed.
best_freqs([X|Xs], CompareXs) ->
best_freqs(Xs, CompareXs, [X]).
best_freqs([], _CompareXs, Bests) ->
Bests;
best_freqs([ {_Name, Count}=X | Xs], CompareXs, [ {_BestName, BestCount} | _]=Bests) ->
case CompareXs(Count, BestCount) of
repl -> best_freqs(Xs, CompareXs, [X]);
add -> best_freqs(Xs, CompareXs, [X|Bests]);
skip -> best_freqs(Xs, CompareXs, Bests)
end.
%-----------------
get_freqs_test() ->
[{scissors,0}, {paper, 0}, {rock, 0}] = get_freqs([]),
[{scissors,1}, {paper, 1}, {rock, 1}] = get_freqs([rock, paper, scissors]),
[{scissors,0}, {paper, 2}, {rock, 2}] = get_freqs([rock, paper, rock, paper]),
[{scissors,2}, {paper, 0}, {rock, 1}] = get_freqs([scissors, scissors, rock]),
[{scissors,0}, {paper, 2}, {rock, 1}] = get_freqs([paper, rock, paper]),
all_tests_passed.
get_freqs(OppPlays) ->
get_freqs(plays(), [], OppPlays).
get_freqs([], Counts, _) ->
Counts;
get_freqs([P|Ps], Counts, OppPlays) ->
io:format("Play: ~w, OppPlays: ~w~n", [P, OppPlays]),
Count = x:count(P, OppPlays),
get_freqs(Ps, [{P, Count} | Counts], OppPlays).
%--------------------------
most_freq_test() ->
Response1 = beats(rock),
Response1 = most_freq([rock, scissors, paper, rock, scissors, rock]),
Response2 = beats(paper),
Response2 = most_freq([rock, paper, paper, scissors]),
Response3 = beats(scissors),
Response3 = most_freq([scissors]), %=> paper, rock will have freqs of 0
Response4 = most_freq([]),
true = w2:member(Response4, plays() ),
all_tests_passed.
most_freq([]) ->
%rand([]);
rock;
most_freq(OppPlays) ->
Freqs = get_freqs(OppPlays),
MaxFunc = fun(X, Y) ->
if
X>Y -> repl;
X=:=Y -> add;
X < Y -> skip
end
end,
MaxFreqs = best_freqs(Freqs, MaxFunc),
%RandNum = random:uniform( x:len(MaxFreqs) ),
%{Play, _Count} = get_elmt(RandNum, MaxFreqs),
[{Play, _Count}|_] = MaxFreqs,
beats(Play).
%------------
apply_rand_strat_test() ->
RandFunc1 = fun(_X) -> 1 end,
RandFunc2 = fun(_X) -> 2 end,
RandFunc3 = fun(_X) -> 3 end,
Strats = [fun rps:no_repeat/1, fun rps:cycle/1, fun rps:least_freq/1],
Plays = [rock, paper],
%Strangely, need module name for pattern match to work:
Strat1 = fun rps:no_repeat/1,
Strat1 = apply_rand_strat(Strats, Plays, RandFunc1),
Strat2 = fun rps:cycle/1,
Strat2 = apply_rand_strat(Strats, Plays, RandFunc2),
Strat3 = fun rps:least_freq/1,
Strat3 = apply_rand_strat(Strats, Plays, RandFunc3),
all_tests_passed.
apply_rand_strat(Ss, Ps) when length(Ss)>0 ->
apply_rand_strat(Ss, Ps, fun random:uniform/1).
apply_rand_strat(Ss, _Ps, RandFunc) ->
RandNum = RandFunc( x:len(Ss) ),
get_elmt(RandNum, Ss).
%-------------
apply_best_strat_test() ->
Strats = [fun rps:cycle/1, fun rps:most_freq/1, fun rps:const/1],
Plays = [rock, paper, rock],
%cycle([]) => rock, rock v. rock => 0
%cycle[[rock]) => paper, paper v. paper => 0
%cycle([paper, rock]) => scissors, scissors v. rock => -1
%
%m_f([]) => rock, rock v. rock => 0
%m_f([rock]) => paper, paper v. paper => 0
%m_f([paper, rock]) => get_freqs() is always in the same order(the reverse
% of plays(): scissors, paper, rock; then best_freqs()
% reverses its results, so most freq in ths case will
% be rock => paper, paper v. rock => +1
%
%const([]) => rock, rock v. rock => 0
%cons([rock]) => rock, rock v. paper => -1
%const([paper, rock]) => rock, rock v. rock => 0
Expected1 = rps:most_freq(Plays),
Expected1 = apply_best_strat(Strats, Plays),
Plays2 = [scissors, paper, scissors],
%cycle([]) -> rock, rock v. scisssors => +1
%cycle([scissors]) -> rock, rock v. paper => -1
%cycl([paper,scissors]) -> rock v. scissors => 1
%
%m_f([]) => rock, rock v. scissors => +1
%m_f([scissors]) => rock, rock v. paper => -1
%m_f([paper, scissors]) => scissors, scissors v. scissors => 0
%
%const([]) => rock, rock v. scissors => +1
%const([scissors]) => rock, rock v. paper => -1
%const([paper, scissors]) => rock, rock v. scissors => +1
Expected2 = rps:cycle(Plays2),
Expected2 = apply_best_strat(Strats, Plays2),
all_tests_passed.
% Variables:
%
% Ss => Strategies
% Ps => Opponent's Plays
% Rs => Responses
%
apply_best_strat(Ss, []) when length(Ss)>0 ->
%RandNum = random:uniform(length(Ss)),
%Strat = get_elmt(RandNum, Ss),
%Strat([]);
[S|_] = Ss,
S([]);
apply_best_strat(Ss, Ps) when length(Ss)>0 ->
Scores = apply_best_strat(Ss, Ps, []),
MaxFunc = fun(X, Y) ->
if
X>Y -> repl;
X=:=Y -> add;
X<Y -> skip
end
end,
[{BestStrat, _Score}|_] = best_freqs(Scores, MaxFunc), %If more than one best strategy...
io:format("BestStrat: ~w~n", [BestStrat]),
BestStrat(Ps). %pick the first one. Needlessly calculates the response again.
apply_best_strat([], _Ps, Scores) ->
Scores;
apply_best_strat([S|Ss], Ps, Scores) ->
Score = backTest(S, Ps), %Funky function name so that eunit doesn't consider it a test.
apply_best_strat(Ss, Ps, [Score|Scores]).
backTest_test() ->
%Test backTest/2 with const():
% const() => rock(_) -> rock
Score1 = {fun rps:const/1, -1},
Score1 = backTest(fun rps:const/1, [paper]),
Score2 = {fun rps:const/1, +1},
Score2 = backTest(fun rps:const/1, [scissors]),
Score3 = {fun rps:const/1, -2},
Score3 = backTest(fun rps:const/1, [paper, rock, paper]),
Score4 = {fun rps:const/1, 0},
Score4 = backTest(fun rps:const/1, [rock, scissors, paper]),
Score5 = {fun rps:const/1, +2},
Score5 = backTest(fun rps:const/1, [scissors, rock, scissors]),
%Can't test least_freq/1, with backTest/2 so test with backTest/4 instead:
History1 = [rock, paper],
Plays1 = [rock],
%least_freq(History1) => scissors, so Response would be rock,
%which draws with the rock in Plays1, thus Score = {least_freq, 0}
Score6 = {fun rps:least_freq/1, 0},
Score6 = backTest(fun rps:least_freq/1, Plays1, History1, []),
History2 = [rock, paper],
Plays2 = [scissors],
Score7 = {fun rps:least_freq/1, 1},
Score7 = backTest(fun rps:least_freq/1, Plays2, History2, []),
History3 = [rock, paper],
Plays3 = [paper, paper],
Score8 = {fun rps:least_freq/1, -2},
Score8 = backTest(fun rps:least_freq/1, Plays3, History3, []),
%Test most_freq/1:
History4 = [],
Plays4 = [rock, paper],
%% l_f([]) => rock, rock v. paper => -1
%% l_f([paper]) => paper, paper v. rock => +1
%% l_f([rock, paper]) -> ends
%% Total => 0
Score9 = {fun rps:least_freq/1, 0},
Score9 = backTest(fun rps:least_freq/1, w2:reverse(Plays4), History4, []),
History5 = [],
Plays5 = [paper, rock, scissors],
%% m_f([]) => rock, rock v. scissors => +1
%% m_f([scissors]) => rock, rock v. rock => 0
%% m_f([rock, scissors]) => paper v. paper => 0
%% m_f([rock, paper]) -> ends
%% Total => -1
Score10 = {fun rps:most_freq/1, +1},
Score10 = backTest(fun rps:most_freq/1, w2:reverse(Plays5), History5, []),
%Test cycle/1:
History6 = [],
Plays6 = [paper, rock, scissors],
%% cycle([]) => rock, rock v. scissors => +1
%% cycle([scissors]) => rock, rock v. rock => 0
%% cycle([rock, scissors]) => paper v. paper => 0
%% cycle([rock, paper]) -> ends
%% Total => +1
Score11 = {fun rps:cycle/1, +1},
Score11 = backTest(fun rps:cycle/1, w2:reverse(Plays6), History6, []),
%%Test rand/1 ???!
%% History7 = [],
%% Plays7 = [paper, rock, scissors],
%% %% rand([]) => rock, rock v. scissors => +1
%% %% rand([scissors]) => rock, rock v. rock => 0
%% %% rand([rock, scissors]) => paper v. paper => 0
%% %% rand([rock, paper]) -> ends
%% %% Total => +1
%% Score12 = {fun rps:rand/1, +1},
%% Score12 = backTest(fun rps:rand/1, w2:reverse(Plays7), History7, []),
all_tests_passed.
backTest(S, Ps) ->
backTest(S, w2:reverse(Ps), [], []).
backTest(S, [], _History, Pairs) ->
%Process the accumulator Pairs:
Score = lists:sum(lists:map(
fun({R, P}) -> outcome(result(R, P) ) end, %R => Response to opponent's previous Play,
Pairs %Acummulator here! %P => opponent's current Play
)),
{S, Score}; %Return Strategy, Score, most recent Response.
backTest(S, [P|Ps], History, Pairs) ->
backTest(S, Ps, [P|History], [{S(History), P}| Pairs]).
%----------
my_hd_test() ->
a = my_hd([a, b, c]),
b = my_hd([b]),
all_tests_passed.
my_hd([X|_]) -> X.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment