Last active
March 12, 2017 21:40
-
-
Save 7-fl/b1081d97196a41186fa0dbfcbebb1006 to your computer and use it in GitHub Desktop.
Finished Implementing tests for apply_best_strat()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -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