Created
March 11, 2017 22:10
-
-
Save 7-fl/dc402b47e4f2092f6b713025cebdf5fa to your computer and use it in GitHub Desktop.
rock/paper/scissors strategies
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: | |
| %% Plays = plays(), | |
| %% Rand = random:uniform(w2:len(Plays)), | |
| %% get_elmt(Rand, Plays); | |
| rand([]); | |
| no_repeat([Play|_]) -> | |
| Others = others(Play), | |
| RandNum = random:uniform(w2: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]). | |
| %--------- | |
| %% Can't test this: | |
| %% | |
| %% const(Ps) -> | |
| %% Plays = plays(), | |
| %% Rand = random:uniform(lists:length(Plays)), | |
| %% get_elmt(Rand, Plays). | |
| rand_test() -> | |
| Plays = plays(), | |
| NumPlays = w2: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 I could test rand/2. | |
| rand(_Ps, RandFunc) -> | |
| Plays = plays(), | |
| RandNum = RandFunc(w2: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: get play after opponent's play in plays() list. | |
| cycle(Plays, plays()). | |
| cycle([], [Response|_Plays]) -> %No opponent plays, response is first play. | |
| Response; | |
| 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([]); | |
| least_freq(Plays) -> | |
| Freqs = get_freqs(Plays), | |
| MinFunc = fun(X, Y) -> %Compare the clarity of an if-stmt to | |
| if %the nested case-stmt below. | |
| X<Y -> repl; | |
| X=:=Y -> add; | |
| X>Y -> skip | |
| end | |
| end, | |
| MinFreqs = freqs_by(Freqs, MinFunc), %There could be more than one... | |
| RandNum = random:uniform( w2:len(MinFreqs) ), %so randomly pick one. | |
| {Play, _Freq} = get_elmt(RandNum,MinFreqs), %Then return whatever Play beats it. | |
| %io:format("~w~n", [Play]), | |
| beats(Play). | |
| %-------------- | |
| freqs_by_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}] = freqs_by([{a,2},{b,1},{c,3}], MinFunc), | |
| [{c,1}, {b,1}] = freqs_by([{a,2},{b,1},{c,1}], MinFunc), | |
| [{c,1}, {b,1}, {a,1}] = freqs_by([{a,1},{b,1},{c,1}], MinFunc), | |
| [{c,3}] = freqs_by([{a,2},{b,1},{c,3}], MaxFunc), | |
| [{c,2}, {a,2}] = freqs_by([{a,2},{b,1},{c,2}], MaxFunc), | |
| [{c,1}, {b,1}, {a,1}] = freqs_by([{a,1},{b,1},{c,1}], MaxFunc), | |
| all_tests_passed. | |
| freqs_by([X|Xs], CompareF) -> | |
| freqs_by(Xs, CompareF, [X]). | |
| freqs_by([], _CompareF, Bests) -> | |
| Bests; | |
| freqs_by([ {_Name, Count}=X | Xs], CompareF, [ {_BestName, BestCount} | _]=Bests) -> | |
| case CompareF(Count, BestCount) of | |
| add -> freqs_by(Xs, CompareF, [X|Bests]); | |
| repl -> freqs_by(Xs, CompareF, [X]); | |
| skip -> freqs_by(Xs, CompareF, 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) -> | |
| Count = x:count(P, OppPlays), | |
| get_freqs(Ps, [{P, Count} | Counts], OppPlays). | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment